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}