malachite_base/unsigned_vector/arithmetic/mod_power_of_2_dot.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::{ModPowerOf2Dot, ModPowerOf2IsReduced};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::unsigned_vector::UnsignedVector;
12
13// The dot product of two vectors modulo $2^k$, which is the wrapping sum of the wrapping products
14// with all but the lowest $k$ bits cleared, since $2^k$ divides $2^W$.
15fn mod_power_of_2_dot<T: PrimitiveUnsigned>(
16 v: &UnsignedVector<T>,
17 w: &UnsignedVector<T>,
18 pow: u64,
19) -> T {
20 assert!(
21 pow <= T::WIDTH,
22 "pow must be at most {}, but is {pow}",
23 T::WIDTH
24 );
25 assert_eq!(
26 v.elements.len(),
27 w.elements.len(),
28 "the vectors must have the same dimension"
29 );
30 assert!(
31 v.mod_power_of_2_is_reduced(pow),
32 "self must be reduced mod 2^{pow}, but {v} has an element >= 2^{pow}"
33 );
34 assert!(
35 w.mod_power_of_2_is_reduced(pow),
36 "other must be reduced mod 2^{pow}, but {w} has an element >= 2^{pow}"
37 );
38 let mut sum = T::ZERO;
39 for (&x, &y) in v.elements.iter().zip(&w.elements) {
40 sum.wrapping_add_assign(x.wrapping_mul(y));
41 }
42 sum.mod_power_of_2(pow)
43}
44
45impl<T: PrimitiveUnsigned> ModPowerOf2Dot<Self> for UnsignedVector<T> {
46 type Output = T;
47
48 /// Computes the dot product of two [`UnsignedVector`]s modulo $2^k$, taking the first by value
49 /// and the second by value. The elements of both must already be reduced.
50 ///
51 /// The dot product of two 0-dimensional vectors is 0.
52 ///
53 /// $$
54 /// f(v, w, k) = \sum_{i=0}^{n-1} v_iw_i \bmod 2^k,
55 /// $$
56 ///
57 /// where $n$ is the dimension of $v$ and $w$.
58 ///
59 /// # Worst-case complexity
60 /// $T(n) = O(n)$
61 ///
62 /// $M(n) = O(1)$
63 ///
64 /// where $T$ is time, $M$ is additional memory, and $n$ is the dimension of the vectors.
65 ///
66 /// # Panics
67 /// Panics if `pow` is greater than `T::WIDTH`, if the vectors have different dimensions, or if
68 /// any element is greater than or equal to $2^k$.
69 ///
70 /// # Examples
71 /// ```
72 /// use core::str::FromStr;
73 /// use malachite_base::num::arithmetic::traits::ModPowerOf2Dot;
74 /// use malachite_base::unsigned_vector::UnsignedVector;
75 ///
76 /// let v = UnsignedVector::<u8>::from_str("(1, 2, 3)").unwrap();
77 /// let w = UnsignedVector::<u8>::from_str("(4, 5, 7)").unwrap();
78 /// assert_eq!(v.clone().mod_power_of_2_dot(w.clone(), 4), 3);
79 /// ```
80 #[inline]
81 fn mod_power_of_2_dot(self, other: Self, pow: u64) -> T {
82 mod_power_of_2_dot(&self, &other, pow)
83 }
84}
85
86impl<T: PrimitiveUnsigned> ModPowerOf2Dot<&Self> for UnsignedVector<T> {
87 type Output = T;
88
89 /// Computes the dot product of two [`UnsignedVector`]s modulo $2^k$, taking the first by value
90 /// and the second by reference. The elements of both must already be reduced.
91 ///
92 /// The dot product of two 0-dimensional vectors is 0.
93 ///
94 /// $$
95 /// f(v, w, k) = \sum_{i=0}^{n-1} v_iw_i \bmod 2^k,
96 /// $$
97 ///
98 /// where $n$ is the dimension of $v$ and $w$.
99 ///
100 /// # Worst-case complexity
101 /// $T(n) = O(n)$
102 ///
103 /// $M(n) = O(1)$
104 ///
105 /// where $T$ is time, $M$ is additional memory, and $n$ is the dimension of the vectors.
106 ///
107 /// # Panics
108 /// Panics if `pow` is greater than `T::WIDTH`, if the vectors have different dimensions, or if
109 /// any element is greater than or equal to $2^k$.
110 ///
111 /// # Examples
112 /// ```
113 /// use core::str::FromStr;
114 /// use malachite_base::num::arithmetic::traits::ModPowerOf2Dot;
115 /// use malachite_base::unsigned_vector::UnsignedVector;
116 ///
117 /// let v = UnsignedVector::<u8>::from_str("(1, 2, 3)").unwrap();
118 /// let w = UnsignedVector::<u8>::from_str("(4, 5, 7)").unwrap();
119 /// assert_eq!(v.clone().mod_power_of_2_dot(&w, 4), 3);
120 /// ```
121 #[inline]
122 fn mod_power_of_2_dot(self, other: &Self, pow: u64) -> T {
123 mod_power_of_2_dot(&self, other, pow)
124 }
125}
126
127impl<T: PrimitiveUnsigned> ModPowerOf2Dot<UnsignedVector<T>> for &UnsignedVector<T> {
128 type Output = T;
129
130 /// Computes the dot product of two [`UnsignedVector`]s modulo $2^k$, taking the first by
131 /// reference and the second by value. The elements of both must already be reduced.
132 ///
133 /// The dot product of two 0-dimensional vectors is 0.
134 ///
135 /// $$
136 /// f(v, w, k) = \sum_{i=0}^{n-1} v_iw_i \bmod 2^k,
137 /// $$
138 ///
139 /// where $n$ is the dimension of $v$ and $w$.
140 ///
141 /// # Worst-case complexity
142 /// $T(n) = O(n)$
143 ///
144 /// $M(n) = O(1)$
145 ///
146 /// where $T$ is time, $M$ is additional memory, and $n$ is the dimension of the vectors.
147 ///
148 /// # Panics
149 /// Panics if `pow` is greater than `T::WIDTH`, if the vectors have different dimensions, or if
150 /// any element is greater than or equal to $2^k$.
151 ///
152 /// # Examples
153 /// ```
154 /// use core::str::FromStr;
155 /// use malachite_base::num::arithmetic::traits::ModPowerOf2Dot;
156 /// use malachite_base::unsigned_vector::UnsignedVector;
157 ///
158 /// let v = UnsignedVector::<u8>::from_str("(1, 2, 3)").unwrap();
159 /// let w = UnsignedVector::<u8>::from_str("(4, 5, 7)").unwrap();
160 /// assert_eq!((&v).mod_power_of_2_dot(w.clone(), 4), 3);
161 /// ```
162 #[inline]
163 fn mod_power_of_2_dot(self, other: UnsignedVector<T>, pow: u64) -> T {
164 mod_power_of_2_dot(self, &other, pow)
165 }
166}
167
168impl<T: PrimitiveUnsigned> ModPowerOf2Dot<&UnsignedVector<T>> for &UnsignedVector<T> {
169 type Output = T;
170
171 /// Computes the dot product of two [`UnsignedVector`]s modulo $2^k$, taking the first by
172 /// reference and the second by reference. The elements of both must already be reduced.
173 ///
174 /// The dot product of two 0-dimensional vectors is 0.
175 ///
176 /// $$
177 /// f(v, w, k) = \sum_{i=0}^{n-1} v_iw_i \bmod 2^k,
178 /// $$
179 ///
180 /// where $n$ is the dimension of $v$ and $w$.
181 ///
182 /// # Worst-case complexity
183 /// $T(n) = O(n)$
184 ///
185 /// $M(n) = O(1)$
186 ///
187 /// where $T$ is time, $M$ is additional memory, and $n$ is the dimension of the vectors.
188 ///
189 /// # Panics
190 /// Panics if `pow` is greater than `T::WIDTH`, if the vectors have different dimensions, or if
191 /// any element is greater than or equal to $2^k$.
192 ///
193 /// # Examples
194 /// ```
195 /// use core::str::FromStr;
196 /// use malachite_base::num::arithmetic::traits::ModPowerOf2Dot;
197 /// use malachite_base::unsigned_vector::UnsignedVector;
198 ///
199 /// let v = UnsignedVector::<u8>::from_str("(1, 2, 3)").unwrap();
200 /// let w = UnsignedVector::<u8>::from_str("(4, 5, 7)").unwrap();
201 /// assert_eq!((&v).mod_power_of_2_dot(&w, 4), 3);
202 /// ```
203 #[inline]
204 fn mod_power_of_2_dot(self, other: &UnsignedVector<T>, pow: u64) -> T {
205 mod_power_of_2_dot(self, other, pow)
206 }
207}