malachite_base/num/arithmetic/mod_power_of_2_shl.rs
1// Copyright © 2025 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::{ModPowerOf2Shl, ModPowerOf2ShlAssign, UnsignedAbs};
10use crate::num::basic::integers::PrimitiveInt;
11use crate::num::basic::signeds::PrimitiveSigned;
12use crate::num::basic::unsigneds::PrimitiveUnsigned;
13use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
14
15fn mod_power_of_2_shl_unsigned<T: PrimitiveUnsigned + Shl<U, Output = T>, U: PrimitiveUnsigned>(
16 x: T,
17 other: U,
18 pow: u64,
19) -> T {
20 assert!(pow <= T::WIDTH);
21 assert!(
22 x.significant_bits() <= pow,
23 "x must be reduced mod 2^pow, but {x} >= 2^{pow}"
24 );
25 if other >= U::exact_from(T::WIDTH) {
26 T::ZERO
27 } else {
28 (x << other).mod_power_of_2(pow)
29 }
30}
31
32fn mod_power_of_2_shl_assign_unsigned<T: PrimitiveUnsigned + ShlAssign<U>, U: PrimitiveUnsigned>(
33 x: &mut T,
34 other: U,
35 pow: u64,
36) {
37 assert!(pow <= T::WIDTH);
38 assert!(
39 x.significant_bits() <= pow,
40 "x must be reduced mod 2^pow, but {x} >= 2^{pow}"
41 );
42 if other >= U::exact_from(T::WIDTH) {
43 *x = T::ZERO;
44 } else {
45 *x <<= other;
46 x.mod_power_of_2_assign(pow);
47 }
48}
49
50macro_rules! impl_mod_power_of_2_shl_unsigned {
51 ($t:ident) => {
52 macro_rules! impl_mod_power_of_2_shl_unsigned_inner {
53 ($u:ident) => {
54 impl ModPowerOf2Shl<$u> for $t {
55 type Output = $t;
56
57 /// Left-shifts a number (multiplies it by a power of 2) modulo $2^k$. The
58 /// number must be already reduced modulo $2^k$.
59 ///
60 /// $f(x, n, k) = y$, where $x, y < 2^k$ and $2^nx \equiv y \mod 2^k$.
61 ///
62 /// # Worst-case complexity
63 /// Constant time and additional memory.
64 ///
65 /// # Panics
66 /// Panics if `pow` is greater than `Self::WIDTH` or if `self` is greater than
67 /// or equal to $2^k$.
68 ///
69 /// # Examples
70 /// See [here](super::mod_power_of_2_shl#mod_power_of_2_shl).
71 #[inline]
72 fn mod_power_of_2_shl(self, other: $u, pow: u64) -> $t {
73 mod_power_of_2_shl_unsigned(self, other, pow)
74 }
75 }
76
77 impl ModPowerOf2ShlAssign<$u> for $t {
78 /// Left-shifts a number (multiplies it by a power of 2) modulo $2^k$, in place.
79 /// The number must be already reduced modulo $2^k$.
80 ///
81 /// $x \gets y$, where $x, y < 2^k$ and $2^nx \equiv y \mod 2^k$.
82 ///
83 /// # Worst-case complexity
84 /// Constant time and additional memory.
85 ///
86 /// # Panics
87 /// Panics if `pow` is greater than `Self::WIDTH` or if `self` is greater than
88 /// or equal to $2^k$.
89 ///
90 /// # Examples
91 /// See [here](super::mod_power_of_2_shl#mod_power_of_2_shl_assign).
92 #[inline]
93 fn mod_power_of_2_shl_assign(&mut self, other: $u, pow: u64) {
94 mod_power_of_2_shl_assign_unsigned(self, other, pow);
95 }
96 }
97 };
98 }
99 apply_to_unsigneds!(impl_mod_power_of_2_shl_unsigned_inner);
100 };
101}
102apply_to_unsigneds!(impl_mod_power_of_2_shl_unsigned);
103
104fn mod_power_of_2_shl_signed<
105 T: ModPowerOf2Shl<U, Output = T> + PrimitiveInt + Shr<U, Output = T>,
106 U: PrimitiveUnsigned,
107 S: PrimitiveSigned + UnsignedAbs<Output = U>,
108>(
109 x: T,
110 other: S,
111 pow: u64,
112) -> T {
113 assert!(pow <= T::WIDTH);
114 assert!(
115 x.significant_bits() <= pow,
116 "x must be reduced mod 2^pow, but {x} >= 2^{pow}"
117 );
118 let other_abs = other.unsigned_abs();
119 if other >= S::ZERO {
120 x.mod_power_of_2_shl(other_abs, pow)
121 } else {
122 let width = U::wrapping_from(T::WIDTH);
123 if width != U::ZERO && other_abs >= width {
124 T::ZERO
125 } else {
126 x >> other_abs
127 }
128 }
129}
130
131fn mod_power_of_2_shl_assign_signed<
132 T: ModPowerOf2ShlAssign<U> + PrimitiveInt + ShrAssign<U>,
133 U: PrimitiveUnsigned,
134 S: PrimitiveSigned + UnsignedAbs<Output = U>,
135>(
136 x: &mut T,
137 other: S,
138 pow: u64,
139) {
140 assert!(pow <= T::WIDTH);
141 assert!(
142 x.significant_bits() <= pow,
143 "x must be reduced mod 2^pow, but {x} >= 2^{pow}"
144 );
145 let other_abs = other.unsigned_abs();
146 if other >= S::ZERO {
147 x.mod_power_of_2_shl_assign(other_abs, pow);
148 } else {
149 let width = U::wrapping_from(T::WIDTH);
150 if width != U::ZERO && other_abs >= width {
151 *x = T::ZERO;
152 } else {
153 *x >>= other_abs;
154 }
155 }
156}
157
158macro_rules! impl_mod_power_of_2_shl_signed {
159 ($t:ident) => {
160 macro_rules! impl_mod_power_of_2_shl_signed_inner {
161 ($u:ident) => {
162 impl ModPowerOf2Shl<$u> for $t {
163 type Output = $t;
164
165 /// Left-shifts a number (multiplies it by a power of 2) modulo $2^k$. The
166 /// number must be already reduced modulo $2^k$.
167 ///
168 /// $f(x, n, k) = y$, where $x, y < 2^k$ and $\lfloor 2^nx \rfloor \equiv y \mod
169 /// 2^k$.
170 ///
171 /// # Panics
172 /// Panics if `pow` is greater than `Self::WIDTH` or if `self` is greater than
173 /// or equal to $2^k$.
174 ///
175 /// # Worst-case complexity
176 /// Constant time and additional memory.
177 ///
178 /// # Examples
179 /// See [here](super::mod_power_of_2_shl#mod_power_of_2_shl).
180 #[inline]
181 fn mod_power_of_2_shl(self, other: $u, pow: u64) -> $t {
182 mod_power_of_2_shl_signed(self, other, pow)
183 }
184 }
185
186 impl ModPowerOf2ShlAssign<$u> for $t {
187 /// Left-shifts a number (multiplies it by a power of 2) modulo $2^k$, in place.
188 /// The number must be already reduced modulo $2^k$.
189 ///
190 /// $x \gets y$, where $x, y < 2^k$ and $\lfloor 2^nx \rfloor \equiv y \mod
191 /// 2^k$.
192 ///
193 /// # Worst-case complexity
194 /// Constant time and additional memory.
195 ///
196 /// # Panics
197 /// Panics if `pow` is greater than `Self::WIDTH` or if `self` is greater than
198 /// or equal to $2^k$.
199 ///
200 /// # Examples
201 /// See [here](super::mod_power_of_2_shl#mod_power_of_2_shl_assign).
202 #[inline]
203 fn mod_power_of_2_shl_assign(&mut self, other: $u, pow: u64) {
204 mod_power_of_2_shl_assign_signed(self, other, pow);
205 }
206 }
207 };
208 }
209 apply_to_signeds!(impl_mod_power_of_2_shl_signed_inner);
210 };
211}
212apply_to_unsigneds!(impl_mod_power_of_2_shl_signed);