Skip to main content

malachite_base/unsigned_polynomial/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_polynomial::UnsignedPolynomial;
12
13fn assert_reduced<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, m: T) {
14    assert!(
15        p.mod_is_reduced(&m),
16        "self must be reduced mod m, but {p} has a coefficient >= {m}"
17    );
18}
19
20// Multiplies every coefficient by 2^bits mod m, which is computed once. A nonzero polynomial
21// reduced modulo m has a leading coefficient that is at least 1 and less than m, so m is at least 2
22// and 1 is reduced modulo m. Since m need not be odd, a product can be zero, so the result is
23// trimmed.
24fn mod_shl_assign<T: PrimitiveUnsigned + ModShl<U, T, Output = T>, U: PrimitiveUnsigned>(
25    p: &mut UnsignedPolynomial<T>,
26    bits: U,
27    m: T,
28) {
29    assert_reduced(p, m);
30    if bits == U::ZERO || p.coefficients.is_empty() {
31        return;
32    }
33    let factor = T::ONE.mod_shl(bits, m);
34    for c in &mut p.coefficients {
35        *c = c.mod_mul(factor, m);
36    }
37    p.trim();
38}
39
40fn mod_shl_ref<T: PrimitiveUnsigned + ModShl<U, T, Output = T>, U: PrimitiveUnsigned>(
41    p: &UnsignedPolynomial<T>,
42    bits: U,
43    m: T,
44) -> UnsignedPolynomial<T> {
45    assert_reduced(p, m);
46    if bits == U::ZERO || p.coefficients.is_empty() {
47        return p.clone();
48    }
49    let factor = T::ONE.mod_shl(bits, m);
50    let mut q = UnsignedPolynomial {
51        coefficients: p
52            .coefficients
53            .iter()
54            .map(|&c| c.mod_mul(factor, m))
55            .collect(),
56    };
57    q.trim();
58    q
59}
60
61macro_rules! impl_mod_shl_unsigned {
62    ($t:ident) => {
63        impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShl<$t, T>
64            for UnsignedPolynomial<T>
65        {
66            type Output = UnsignedPolynomial<T>;
67
68            /// Left-shifts an [`UnsignedPolynomial`] (multiplies it by a power of 2) modulo `m`,
69            /// taking the polynomial by value. The coefficients must already be reduced modulo `m`.
70            ///
71            /// $2^k \bmod m$ is computed once, and every coefficient is multiplied by it. Since `m`
72            /// need not be odd, coefficients can become zero, so the degree can drop.
73            ///
74            /// $$
75            /// f(p, k, m) = 2^kp \bmod m.
76            /// $$
77            ///
78            /// # Worst-case complexity
79            /// $T(n, m) = O(n + m)$
80            ///
81            /// $M(n) = O(1)$
82            ///
83            /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, and $m$ is
84            /// `bits.significant_bits()`.
85            ///
86            /// # Panics
87            /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
88            ///
89            /// # Examples
90            /// See [here](super::mod_shl#mod_shl).
91            #[inline]
92            fn mod_shl(mut self, bits: $t, m: T) -> UnsignedPolynomial<T> {
93                mod_shl_assign(&mut self, bits, m);
94                self
95            }
96        }
97
98        impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShl<$t, T>
99            for &UnsignedPolynomial<T>
100        {
101            type Output = UnsignedPolynomial<T>;
102
103            /// Left-shifts an [`UnsignedPolynomial`] (multiplies it by a power of 2) modulo `m`,
104            /// taking the polynomial by reference. The coefficients must already be reduced modulo
105            /// `m`.
106            ///
107            /// $2^k \bmod m$ is computed once, and every coefficient is multiplied by it. Since `m`
108            /// need not be odd, coefficients can become zero, so the degree can drop.
109            ///
110            /// $$
111            /// f(p, k, m) = 2^kp \bmod m.
112            /// $$
113            ///
114            /// # Worst-case complexity
115            /// $T(n, m) = O(n + m)$
116            ///
117            /// $M(n) = O(n)$
118            ///
119            /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, and $m$ is
120            /// `bits.significant_bits()`.
121            ///
122            /// # Panics
123            /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
124            ///
125            /// # Examples
126            /// See [here](super::mod_shl#mod_shl).
127            #[inline]
128            fn mod_shl(self, bits: $t, m: T) -> UnsignedPolynomial<T> {
129                mod_shl_ref(self, bits, m)
130            }
131        }
132
133        impl<T: PrimitiveUnsigned + ModShl<$t, T, Output = T>> ModShlAssign<$t, T>
134            for UnsignedPolynomial<T>
135        {
136            /// Left-shifts an [`UnsignedPolynomial`] (multiplies it by a power of 2) modulo `m`, in
137            /// place. The coefficients must already be reduced modulo `m`.
138            ///
139            /// $2^k \bmod m$ is computed once, and every coefficient is multiplied by it. Since `m`
140            /// need not be odd, coefficients can become zero, so the degree can drop.
141            ///
142            /// $$
143            /// p \gets 2^kp \bmod m.
144            /// $$
145            ///
146            /// # Worst-case complexity
147            /// $T(n, m) = O(n + m)$
148            ///
149            /// $M(n) = O(1)$
150            ///
151            /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, and $m$ is
152            /// `bits.significant_bits()`.
153            ///
154            /// # Panics
155            /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
156            ///
157            /// # Examples
158            /// See [here](super::mod_shl#mod_shl_assign).
159            #[inline]
160            fn mod_shl_assign(&mut self, bits: $t, m: T) {
161                mod_shl_assign(self, bits, m);
162            }
163        }
164    };
165}
166apply_to_unsigneds!(impl_mod_shl_unsigned);