Skip to main content

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);