Skip to main content

crypto_bigint/int/
shl.rs

1//! [`Int`] bitwise left shift operations.
2
3use crate::{CtOption, Int, ShlVartime, Uint, WrappingShl};
4use core::ops::{Shl, ShlAssign};
5
6impl<const LIMBS: usize> Int<LIMBS> {
7    /// Computes `self << shift`.
8    ///
9    /// # Panics
10    /// - if `shift >= Self::BITS`.
11    #[inline]
12    #[must_use]
13    #[track_caller]
14    pub const fn shl(&self, shift: u32) -> Self {
15        Self(Uint::shl(&self.0, shift))
16    }
17
18    /// Computes `self << shift` in variable time.
19    ///
20    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
21    ///
22    /// When used with a fixed `shift`, this function is constant-time with respect to `self`.
23    ///
24    /// # Panics
25    /// - if `shift >= Self::BITS`.
26    #[inline(always)]
27    #[must_use]
28    #[track_caller]
29    pub const fn shl_vartime(&self, shift: u32) -> Self {
30        Self(Uint::shl_vartime(&self.0, shift))
31    }
32
33    /// Computes `self << shift`.
34    ///
35    /// Returns `None` if `shift >= Self::BITS`.
36    #[inline(always)]
37    #[must_use]
38    pub const fn overflowing_shl(&self, shift: u32) -> CtOption<Self> {
39        Self::from_uint_opt(self.0.overflowing_shl(shift))
40    }
41
42    /// Computes `self << shift`.
43    ///
44    /// Returns `None` if `shift >= Self::BITS`.
45    ///
46    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
47    ///
48    /// When used with a fixed `shift`, this function is constant-time with respect
49    /// to `self`.
50    #[inline(always)]
51    #[must_use]
52    pub const fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
53        if let Some(uint) = self.0.overflowing_shl_vartime(shift) {
54            Some(*uint.as_int())
55        } else {
56            None
57        }
58    }
59
60    /// Computes `self << shift` in a panic-free manner, returning zero if the shift exceeds the
61    /// precision.
62    #[inline(always)]
63    #[must_use]
64    pub const fn unbounded_shl(&self, shift: u32) -> Self {
65        Self(self.0.unbounded_shl(shift))
66    }
67
68    /// Computes `self << shift` in variable-time in a panic-free manner, returning zero if the
69    /// shift exceeds the precision.
70    ///
71    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
72    ///
73    /// When used with a fixed `shift`, this function is constant-time with respect to `self`.
74    #[inline(always)]
75    #[must_use]
76    pub const fn unbounded_shl_vartime(&self, shift: u32) -> Self {
77        Self(self.0.unbounded_shl_vartime(shift))
78    }
79
80    /// Computes `self << shift` in a panic-free manner, reducing shift modulo the type's width.
81    #[inline(always)]
82    #[must_use]
83    pub const fn wrapping_shl(&self, shift: u32) -> Self {
84        Self(self.0.wrapping_shl(shift))
85    }
86
87    /// Computes `self << shift` in variable-time in a panic-free manner, reducing shift modulo
88    /// the type's width.
89    ///
90    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
91    ///
92    /// When used with a fixed `shift`, this function is constant-time with respect to `self`.
93    #[inline(always)]
94    #[must_use]
95    pub const fn wrapping_shl_vartime(&self, shift: u32) -> Self {
96        Self(self.0.wrapping_shl_vartime(shift))
97    }
98}
99
100macro_rules! impl_shl {
101    ($($shift:ty),+) => {
102        $(
103            impl<const LIMBS: usize> Shl<$shift> for Int<LIMBS> {
104                type Output = Int<LIMBS>;
105
106                #[inline]
107                fn shl(self, shift: $shift) -> Int<LIMBS> {
108                    <&Self>::shl(&self, shift)
109                }
110            }
111
112            impl<const LIMBS: usize> Shl<$shift> for &Int<LIMBS> {
113                type Output = Int<LIMBS>;
114
115                #[inline]
116                fn shl(self, shift: $shift) -> Int<LIMBS> {
117                    Int::<LIMBS>::shl(self, u32::try_from(shift).expect("invalid shift"))
118                }
119            }
120
121            impl<const LIMBS: usize> ShlAssign<$shift> for Int<LIMBS> {
122                fn shl_assign(&mut self, shift: $shift) {
123                    *self = self.shl(shift)
124                }
125            }
126        )+
127    };
128}
129
130impl_shl!(i32, u32, usize);
131
132impl<const LIMBS: usize> WrappingShl for Int<LIMBS> {
133    fn wrapping_shl(&self, shift: u32) -> Int<LIMBS> {
134        self.wrapping_shl(shift)
135    }
136}
137
138impl<const LIMBS: usize> ShlVartime for Int<LIMBS> {
139    fn overflowing_shl_vartime(&self, shift: u32) -> Option<Self> {
140        self.overflowing_shl_vartime(shift)
141    }
142
143    fn unbounded_shl_vartime(&self, shift: u32) -> Self {
144        self.unbounded_shl_vartime(shift)
145    }
146
147    fn wrapping_shl_vartime(&self, shift: u32) -> Self {
148        self.wrapping_shl_vartime(shift)
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use crate::{I256, ShlVartime};
155
156    const N: I256 =
157        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
158
159    const TWO_N: I256 =
160        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C8282");
161
162    const FOUR_N: I256 =
163        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAEABB739ABD2280EEFF497A3340D90504");
164
165    const SIXTY_FIVE: I256 =
166        I256::from_be_hex("FFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C82820000000000000000");
167
168    const EIGHTY_EIGHT: I256 =
169        I256::from_be_hex("FFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD03641410000000000000000000000");
170
171    const SIXTY_FOUR: I256 =
172        I256::from_be_hex("FFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD03641410000000000000000");
173
174    #[test]
175    fn shl_simple() {
176        let mut t = I256::from(1i8);
177        assert_eq!(t << 1, I256::from(2i8));
178        t = I256::from(3i8);
179        assert_eq!(t << 8, I256::from(0x300i16));
180    }
181
182    #[test]
183    fn shl1() {
184        assert_eq!(N << 1, TWO_N);
185        assert_eq!(ShlVartime::overflowing_shl_vartime(&N, 1), Some(TWO_N));
186        assert_eq!(ShlVartime::wrapping_shl_vartime(&N, 1), TWO_N);
187    }
188
189    #[test]
190    fn shl2() {
191        assert_eq!(N << 2, FOUR_N);
192    }
193
194    #[test]
195    fn shl65() {
196        assert_eq!(N << 65, SIXTY_FIVE);
197    }
198
199    #[test]
200    fn shl88() {
201        assert_eq!(N << 88, EIGHTY_EIGHT);
202    }
203
204    #[test]
205    fn shl256_const() {
206        assert!(N.overflowing_shl(256).is_none().to_bool_vartime());
207        assert!(ShlVartime::overflowing_shl_vartime(&N, 256).is_none());
208    }
209
210    #[test]
211    #[should_panic(expected = "`shift` exceeds upper bound")]
212    fn shl_bounds_panic() {
213        let _ = N << 256;
214    }
215
216    #[test]
217    fn shl64() {
218        assert_eq!(N << 64, SIXTY_FOUR);
219    }
220
221    #[test]
222    fn unbounded_shl() {
223        assert_eq!(I256::MAX.unbounded_shl(257), I256::ZERO);
224        assert_eq!(I256::MIN.unbounded_shl(257), I256::ZERO);
225        assert_eq!(
226            ShlVartime::unbounded_shl_vartime(&I256::MAX, 257),
227            I256::ZERO
228        );
229        assert_eq!(
230            ShlVartime::unbounded_shl_vartime(&I256::MIN, 257),
231            I256::ZERO
232        );
233    }
234
235    #[test]
236    fn wrapping_shl() {
237        assert_eq!(I256::MAX.wrapping_shl(257), I256::MAX.shl(1));
238        assert_eq!(I256::MIN.wrapping_shl(257), I256::MIN.shl(1));
239        assert_eq!(
240            ShlVartime::wrapping_shl_vartime(&I256::MAX, 257),
241            I256::MAX.shl(1)
242        );
243        assert_eq!(
244            ShlVartime::wrapping_shl_vartime(&I256::MIN, 257),
245            I256::MIN.shl(1)
246        );
247    }
248}