malachite_base/unsigned_vector/arithmetic/mod_shl.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::{ModIsReduced, ModShl, ModShlAssign};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::unsigned_vector::UnsignedVector;
12use crate::unsigned_vector::arithmetic::mod_mul::{mod_mul_assign_unchecked, mod_mul_unchecked};
13
14fn assert_reduced<T: PrimitiveUnsigned>(v: &UnsignedVector<T>, m: T) {
15 assert!(
16 v.mod_is_reduced(&m),
17 "self must be reduced mod m, but {v} has an element >= {m}"
18 );
19}
20
21// Whether shifting changes nothing: a shift by 0, an empty vector, or a modulus of 1, modulo which
22// every element is 0. Otherwise the vector has an element, which is less than `m`, so `m` is at
23// least 2 and 1 is reduced modulo `m`.
24fn is_unchanged<T: PrimitiveUnsigned, U: PrimitiveUnsigned>(
25 v: &UnsignedVector<T>,
26 bits: U,
27 m: T,
28) -> bool {
29 bits == U::ZERO || v.elements.is_empty() || m == T::ONE
30}
31
32// Multiplies every element by 2^bits mod m, which is computed once.
33fn mod_shl_assign<T: PrimitiveUnsigned + ModShl<U, T, Output = T>, U: PrimitiveUnsigned>(
34 v: &mut UnsignedVector<T>,
35 bits: U,
36 m: T,
37) {
38 assert_reduced(v, m);
39 if !is_unchanged(v, bits, m) {
40 mod_mul_assign_unchecked(&mut v.elements, T::ONE.mod_shl(bits, m), m);
41 }
42}
43
44fn mod_shl_ref<T: PrimitiveUnsigned + ModShl<U, T, Output = T>, U: PrimitiveUnsigned>(
45 v: &UnsignedVector<T>,
46 bits: U,
47 m: T,
48) -> UnsignedVector<T> {
49 assert_reduced(v, m);
50 if is_unchanged(v, bits, m) {
51 return v.clone();
52 }
53 UnsignedVector {
54 elements: mod_mul_unchecked(&v.elements, T::ONE.mod_shl(bits, m), m),
55 }
56}
57
58macro_rules! impl_mod_shl_unsigned {
59 ($t:ident) => {
60 impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShl<$t, T> for UnsignedVector<T> {
61 type Output = UnsignedVector<T>;
62
63 /// Left-shifts an [`UnsignedVector`] (multiplies it by a power of 2) modulo $m$, taking
64 /// the vector by value. The elements must already be reduced modulo $m$.
65 ///
66 /// $2^k \bmod m$ is computed once, and every element is multiplied by it, with the data
67 /// for multiplying modulo $m$ also computed once. Since $m$ need not be odd, elements
68 /// can become zero; the dimension is unchanged.
69 ///
70 /// $$
71 /// f(v, k, m) = 2^kv \bmod m.
72 /// $$
73 ///
74 /// # Worst-case complexity
75 /// $T(n, k) = O(n + k)$
76 ///
77 /// $M(n) = O(1)$
78 ///
79 /// where $T$ is time, $M$ is additional memory, $n$ is `self.dimension()`, and $k$ is
80 /// `bits.significant_bits()`.
81 ///
82 /// # Panics
83 /// Panics if `m` is 0, or if any element of `self` is greater than or equal to `m`.
84 ///
85 /// # Examples
86 /// See [here](super::mod_shl#mod_shl).
87 #[inline]
88 fn mod_shl(mut self, bits: $t, m: T) -> UnsignedVector<T> {
89 mod_shl_assign(&mut self, bits, m);
90 self
91 }
92 }
93
94 impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShl<$t, T>
95 for &UnsignedVector<T>
96 {
97 type Output = UnsignedVector<T>;
98
99 /// Left-shifts an [`UnsignedVector`] (multiplies it by a power of 2) modulo $m$, taking
100 /// the vector by reference. The elements must already be reduced modulo $m$.
101 ///
102 /// See the documentation for the [`ModShl`] implementation that takes everything by
103 /// value for details.
104 ///
105 /// # Worst-case complexity
106 /// $T(n, k) = O(n + k)$
107 ///
108 /// $M(n) = O(n)$
109 ///
110 /// where $T$ is time, $M$ is additional memory, $n$ is `self.dimension()`, and $k$ is
111 /// `bits.significant_bits()`.
112 ///
113 /// # Panics
114 /// Panics if `m` is 0, or if any element of `self` is greater than or equal to `m`.
115 ///
116 /// # Examples
117 /// See [here](super::mod_shl#mod_shl).
118 #[inline]
119 fn mod_shl(self, bits: $t, m: T) -> UnsignedVector<T> {
120 mod_shl_ref(self, bits, m)
121 }
122 }
123
124 impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShlAssign<$t, T>
125 for UnsignedVector<T>
126 {
127 /// Left-shifts an [`UnsignedVector`] (multiplies it by a power of 2) modulo $m$, in
128 /// place. The elements must already be reduced modulo $m$.
129 ///
130 /// See the documentation for the [`ModShl`] implementation that takes everything by
131 /// value for details.
132 ///
133 /// # Worst-case complexity
134 /// $T(n, k) = O(n + k)$
135 ///
136 /// $M(n) = O(1)$
137 ///
138 /// where $T$ is time, $M$ is additional memory, $n$ is `self.dimension()`, and $k$ is
139 /// `bits.significant_bits()`.
140 ///
141 /// # Panics
142 /// Panics if `m` is 0, or if any element of `self` is greater than or equal to `m`.
143 ///
144 /// # Examples
145 /// See [here](super::mod_shl#mod_shl_assign).
146 #[inline]
147 fn mod_shl_assign(&mut self, bits: $t, m: T) {
148 mod_shl_assign(self, bits, m);
149 }
150 }
151 };
152}
153apply_to_unsigneds!(impl_mod_shl_unsigned);