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