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