Skip to main content

malachite_base/unsigned_vector/arithmetic/
mod_add_mul.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{ModAddMul, ModAddMulAssign, ModIsReduced};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::unsigned_vector::UnsignedVector;
12
13fn assert_reduced<T: PrimitiveUnsigned>(v: &UnsignedVector<T>, w: &UnsignedVector<T>, c: T, m: T) {
14    assert_eq!(
15        v.elements.len(),
16        w.elements.len(),
17        "cannot add-multiply vectors of different dimensions"
18    );
19    assert!(
20        v.mod_is_reduced(&m),
21        "self must be reduced mod m, but {v} has an element >= {m}"
22    );
23    assert!(
24        w.mod_is_reduced(&m),
25        "w must be reduced mod m, but {w} has an element >= {m}"
26    );
27    assert!(
28        c.mod_is_reduced(&m),
29        "c must be reduced mod m, but {c} >= {m}"
30    );
31}
32
33// Adds `ys` times `c` to `xs` modulo `m`, in place, for inputs already known to be reduced. Like
34// FLINT, this handles `c` equal to 0, 1, and `m - 1` directly, and otherwise computes the data for
35// multiplying modulo `m` once and reuses it for every element.
36fn mod_add_mul_assign_unchecked<T: PrimitiveUnsigned>(xs: &mut [T], ys: &[T], c: T, m: T) {
37    if c == T::ZERO {
38        return;
39    }
40    if c == T::ONE {
41        for (x, &y) in xs.iter_mut().zip(ys) {
42            x.mod_add_assign(y, m);
43        }
44    } else if c == m - T::ONE {
45        for (x, &y) in xs.iter_mut().zip(ys) {
46            x.mod_sub_assign(y, m);
47        }
48    } else {
49        let data = T::precompute_mod_mul_data(&m);
50        for (x, &y) in xs.iter_mut().zip(ys) {
51            x.mod_add_assign(y.mod_mul_precomputed(c, m, &data), m);
52        }
53    }
54}
55
56impl<T: PrimitiveUnsigned> ModAddMul<Self, T, T> for UnsignedVector<T> {
57    type Output = Self;
58
59    /// Adds a scalar multiple of an [`UnsignedVector`] to an [`UnsignedVector`] modulo $m$, taking
60    /// both vectors by value. The elements and the scalar must already be reduced modulo $m$.
61    ///
62    /// The operation is taken element by element, so the result has the same dimension as the
63    /// vectors.
64    ///
65    /// $$
66    /// f(v, w, c, m) = (v + cw) \bmod m.
67    /// $$
68    ///
69    /// # Worst-case complexity
70    /// $T(n) = O(n)$
71    ///
72    /// $M(n) = O(1)$
73    ///
74    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
75    ///
76    /// # Panics
77    /// Panics if `self` and `w` have different dimensions, or if any element of `self` or `w`, or
78    /// `c`, is greater than or equal to `m`.
79    ///
80    /// # Examples
81    /// ```
82    /// use core::str::FromStr;
83    /// use malachite_base::num::arithmetic::traits::ModAddMul;
84    /// use malachite_base::unsigned_vector::UnsignedVector;
85    ///
86    /// let v = UnsignedVector::<u8>::from_str("(5, 1, 3)").unwrap();
87    /// let w = UnsignedVector::<u8>::from_str("(2, 6, 4)").unwrap();
88    /// assert_eq!(v.mod_add_mul(w, 3, 7).to_string(), "(4, 5, 1)");
89    /// ```
90    ///
91    /// This is equivalent to `_nmod_vec_scalar_addmul_nmod` from `nmod_vec/scalar.c`, FLINT 3.6.0.
92    #[inline]
93    fn mod_add_mul(mut self, w: Self, c: T, m: T) -> Self {
94        self.mod_add_mul_assign(&w, c, m);
95        self
96    }
97}
98
99impl<T: PrimitiveUnsigned> ModAddMul<&Self, T, T> for UnsignedVector<T> {
100    type Output = Self;
101
102    /// Adds a scalar multiple of an [`UnsignedVector`] to an [`UnsignedVector`] modulo $m$, taking
103    /// the first vector by value and the second by reference. The elements and the scalar must
104    /// already be reduced modulo $m$.
105    ///
106    /// See the documentation for the [`ModAddMul`] implementation that takes both vectors by value
107    /// for details.
108    ///
109    /// # Worst-case complexity
110    /// $T(n) = O(n)$
111    ///
112    /// $M(n) = O(1)$
113    ///
114    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
115    ///
116    /// # Panics
117    /// Panics if `self` and `w` have different dimensions, or if any element of `self` or `w`, or
118    /// `c`, is greater than or equal to `m`.
119    ///
120    /// # Examples
121    /// ```
122    /// use core::str::FromStr;
123    /// use malachite_base::num::arithmetic::traits::ModAddMul;
124    /// use malachite_base::unsigned_vector::UnsignedVector;
125    ///
126    /// let v = UnsignedVector::<u8>::from_str("(5, 1, 3)").unwrap();
127    /// let w = UnsignedVector::<u8>::from_str("(2, 6, 4)").unwrap();
128    /// assert_eq!(v.mod_add_mul(&w, 3, 7).to_string(), "(4, 5, 1)");
129    /// ```
130    ///
131    /// This is equivalent to `_nmod_vec_scalar_addmul_nmod` from `nmod_vec/scalar.c`, FLINT 3.6.0.
132    #[inline]
133    fn mod_add_mul(mut self, w: &Self, c: T, m: T) -> Self {
134        self.mod_add_mul_assign(w, c, m);
135        self
136    }
137}
138
139impl<T: PrimitiveUnsigned> ModAddMul<&UnsignedVector<T>, T, T> for &UnsignedVector<T> {
140    type Output = UnsignedVector<T>;
141
142    /// Adds a scalar multiple of an [`UnsignedVector`] to an [`UnsignedVector`] modulo $m$, taking
143    /// both vectors by reference. The elements and the scalar must already be reduced modulo $m$.
144    ///
145    /// See the documentation for the [`ModAddMul`] implementation that takes both vectors by value
146    /// for details.
147    ///
148    /// # Worst-case complexity
149    /// $T(n) = O(n)$
150    ///
151    /// $M(n) = O(n)$
152    ///
153    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
154    ///
155    /// # Panics
156    /// Panics if `self` and `w` have different dimensions, or if any element of `self` or `w`, or
157    /// `c`, is greater than or equal to `m`.
158    ///
159    /// # Examples
160    /// ```
161    /// use core::str::FromStr;
162    /// use malachite_base::num::arithmetic::traits::ModAddMul;
163    /// use malachite_base::unsigned_vector::UnsignedVector;
164    ///
165    /// let v = UnsignedVector::<u8>::from_str("(5, 1, 3)").unwrap();
166    /// let w = UnsignedVector::<u8>::from_str("(2, 6, 4)").unwrap();
167    /// assert_eq!((&v).mod_add_mul(&w, 3, 7).to_string(), "(4, 5, 1)");
168    /// ```
169    ///
170    /// This is equivalent to `_nmod_vec_scalar_addmul_nmod` from `nmod_vec/scalar.c`, FLINT 3.6.0.
171    fn mod_add_mul(self, w: &UnsignedVector<T>, c: T, m: T) -> UnsignedVector<T> {
172        assert_reduced(self, w, c, m);
173        let mut elements = self.elements.clone();
174        mod_add_mul_assign_unchecked(&mut elements, &w.elements, c, m);
175        UnsignedVector { elements }
176    }
177}
178
179impl<T: PrimitiveUnsigned> ModAddMulAssign<Self, T, T> for UnsignedVector<T> {
180    /// Adds a scalar multiple of an [`UnsignedVector`] to an [`UnsignedVector`] modulo $m$, in
181    /// place, taking the vector on the right-hand side by value. The elements and the scalar must
182    /// already be reduced modulo $m$.
183    ///
184    /// See the documentation for the [`ModAddMul`] implementation that takes both vectors by value
185    /// for details.
186    ///
187    /// # Worst-case complexity
188    /// $T(n) = O(n)$
189    ///
190    /// $M(n) = O(1)$
191    ///
192    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
193    ///
194    /// # Panics
195    /// Panics if `self` and `w` have different dimensions, or if any element of `self` or `w`, or
196    /// `c`, is greater than or equal to `m`.
197    ///
198    /// # Examples
199    /// ```
200    /// use core::str::FromStr;
201    /// use malachite_base::num::arithmetic::traits::ModAddMulAssign;
202    /// use malachite_base::unsigned_vector::UnsignedVector;
203    ///
204    /// let mut v = UnsignedVector::<u8>::from_str("(5, 1, 3)").unwrap();
205    /// let w = UnsignedVector::<u8>::from_str("(2, 6, 4)").unwrap();
206    /// v.mod_add_mul_assign(w, 3, 7);
207    /// assert_eq!(v.to_string(), "(4, 5, 1)");
208    /// ```
209    ///
210    /// This is equivalent to `_nmod_vec_scalar_addmul_nmod` from `nmod_vec/scalar.c`, FLINT 3.6.0.
211    #[inline]
212    fn mod_add_mul_assign(&mut self, w: Self, c: T, m: T) {
213        self.mod_add_mul_assign(&w, c, m);
214    }
215}
216
217impl<T: PrimitiveUnsigned> ModAddMulAssign<&Self, T, T> for UnsignedVector<T> {
218    /// Adds a scalar multiple of an [`UnsignedVector`] to an [`UnsignedVector`] modulo $m$, in
219    /// place, taking the vector on the right-hand side by reference. The elements and the scalar
220    /// must already be reduced modulo $m$.
221    ///
222    /// See the documentation for the [`ModAddMul`] implementation that takes both vectors by value
223    /// for details.
224    ///
225    /// # Worst-case complexity
226    /// $T(n) = O(n)$
227    ///
228    /// $M(n) = O(1)$
229    ///
230    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.dimension()`.
231    ///
232    /// # Panics
233    /// Panics if `self` and `w` have different dimensions, or if any element of `self` or `w`, or
234    /// `c`, is greater than or equal to `m`.
235    ///
236    /// # Examples
237    /// ```
238    /// use core::str::FromStr;
239    /// use malachite_base::num::arithmetic::traits::ModAddMulAssign;
240    /// use malachite_base::unsigned_vector::UnsignedVector;
241    ///
242    /// let mut v = UnsignedVector::<u8>::from_str("(5, 1, 3)").unwrap();
243    /// let w = UnsignedVector::<u8>::from_str("(2, 6, 4)").unwrap();
244    /// v.mod_add_mul_assign(&w, 3, 7);
245    /// assert_eq!(v.to_string(), "(4, 5, 1)");
246    /// ```
247    ///
248    /// This is equivalent to `_nmod_vec_scalar_addmul_nmod` from `nmod_vec/scalar.c`, FLINT 3.6.0.
249    fn mod_add_mul_assign(&mut self, w: &Self, c: T, m: T) {
250        assert_reduced(self, w, c, m);
251        mod_add_mul_assign_unchecked(&mut self.elements, &w.elements, c, m);
252    }
253}