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