ibig/
shift_ops.rs

1//! Bit shift operators.
2
3use crate::{
4    arch::word::Word,
5    buffer::Buffer,
6    ibig::IBig,
7    primitive::{double_word, extend_word, split_double_word, WORD_BITS_USIZE},
8    shift,
9    sign::Sign::*,
10    ubig::{Repr::*, UBig},
11};
12use core::{
13    mem,
14    ops::{Shl, ShlAssign, Shr, ShrAssign},
15};
16
17macro_rules! impl_shifts {
18    ($t:ty) => {
19        impl Shl<&usize> for $t {
20            type Output = $t;
21
22            #[inline]
23            fn shl(self, rhs: &usize) -> $t {
24                self.shl(*rhs)
25            }
26        }
27
28        impl Shl<&usize> for &$t {
29            type Output = $t;
30
31            #[inline]
32            fn shl(self, rhs: &usize) -> $t {
33                self.shl(*rhs)
34            }
35        }
36
37        impl ShlAssign<usize> for $t {
38            #[inline]
39            fn shl_assign(&mut self, rhs: usize) {
40                *self = mem::take(self) << rhs;
41            }
42        }
43
44        impl ShlAssign<&usize> for $t {
45            #[inline]
46            fn shl_assign(&mut self, rhs: &usize) {
47                *self = mem::take(self) << rhs;
48            }
49        }
50
51        impl Shr<&usize> for $t {
52            type Output = $t;
53
54            #[inline]
55            fn shr(self, rhs: &usize) -> $t {
56                self.shr(*rhs)
57            }
58        }
59
60        impl Shr<&usize> for &$t {
61            type Output = $t;
62
63            #[inline]
64            fn shr(self, rhs: &usize) -> $t {
65                self.shr(*rhs)
66            }
67        }
68
69        impl ShrAssign<usize> for $t {
70            #[inline]
71            fn shr_assign(&mut self, rhs: usize) {
72                *self = mem::take(self).shr(rhs);
73            }
74        }
75
76        impl ShrAssign<&usize> for $t {
77            #[inline]
78            fn shr_assign(&mut self, rhs: &usize) {
79                *self = mem::take(self).shr(rhs);
80            }
81        }
82    };
83}
84
85impl_shifts!(UBig);
86impl_shifts!(IBig);
87
88impl Shl<usize> for UBig {
89    type Output = UBig;
90
91    #[inline]
92    fn shl(self, rhs: usize) -> UBig {
93        match self.into_repr() {
94            Small(0) => UBig::from_word(0),
95            Small(word) => UBig::shl_word(word, rhs),
96            Large(buffer) => UBig::shl_large(buffer, rhs),
97        }
98    }
99}
100
101impl Shl<usize> for &UBig {
102    type Output = UBig;
103
104    #[inline]
105    fn shl(self, rhs: usize) -> UBig {
106        match self.repr() {
107            Small(0) => UBig::from_word(0),
108            Small(word) => UBig::shl_word(*word, rhs),
109            Large(buffer) => UBig::shl_ref_large(buffer, rhs),
110        }
111    }
112}
113
114impl Shr<usize> for UBig {
115    type Output = UBig;
116
117    #[inline]
118    fn shr(self, rhs: usize) -> UBig {
119        match self.into_repr() {
120            Small(word) => UBig::shr_word(word, rhs),
121            Large(buffer) => UBig::shr_large(buffer, rhs),
122        }
123    }
124}
125
126impl Shr<usize> for &UBig {
127    type Output = UBig;
128
129    #[inline]
130    fn shr(self, rhs: usize) -> UBig {
131        match self.repr() {
132            Small(word) => UBig::shr_word(*word, rhs),
133            Large(buffer) => UBig::shr_large_ref(buffer, rhs),
134        }
135    }
136}
137
138impl Shl<usize> for IBig {
139    type Output = IBig;
140
141    #[inline]
142    fn shl(self, rhs: usize) -> IBig {
143        let (sign, mag) = self.into_sign_magnitude();
144        IBig::from_sign_magnitude(sign, mag.shl(rhs))
145    }
146}
147
148impl Shl<usize> for &IBig {
149    type Output = IBig;
150
151    #[inline]
152    fn shl(self, rhs: usize) -> IBig {
153        let (sign, mag) = (self.sign(), self.magnitude());
154        IBig::from_sign_magnitude(sign, mag.shl(rhs))
155    }
156}
157
158impl Shr<usize> for IBig {
159    type Output = IBig;
160
161    #[inline]
162    fn shr(self, rhs: usize) -> IBig {
163        let (sign, mag) = self.into_sign_magnitude();
164        match sign {
165            Positive => IBig::from(mag.shr(rhs)),
166            Negative => {
167                let b = mag.are_low_bits_nonzero(rhs);
168                -IBig::from(mag.shr(rhs)) - IBig::from(b)
169            }
170        }
171    }
172}
173
174impl Shr<usize> for &IBig {
175    type Output = IBig;
176
177    #[inline]
178    fn shr(self, rhs: usize) -> IBig {
179        let (sign, mag) = (self.sign(), self.magnitude());
180        match sign {
181            Positive => IBig::from(mag.shr(rhs)),
182            Negative => {
183                let b = mag.are_low_bits_nonzero(rhs);
184                -IBig::from(mag.shr(rhs)) - IBig::from(b)
185            }
186        }
187    }
188}
189
190impl UBig {
191    /// Shift left one non-zero `Word` by `rhs` bits.
192    #[inline]
193    fn shl_word(word: Word, rhs: usize) -> UBig {
194        debug_assert!(word != 0);
195
196        if rhs <= WORD_BITS_USIZE {
197            UBig::from(extend_word(word) << rhs)
198        } else {
199            UBig::shl_word_slow(word, rhs)
200        }
201    }
202
203    /// Shift left one non-zero `Word` by `rhs` bits.
204    fn shl_word_slow(word: Word, rhs: usize) -> UBig {
205        let shift_words = rhs / WORD_BITS_USIZE;
206        let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
207        let (lo, hi) = split_double_word(extend_word(word) << shift_bits);
208        let mut buffer = Buffer::allocate(shift_words + 2);
209        buffer.push_zeros(shift_words);
210        buffer.push(lo);
211        buffer.push(hi);
212        buffer.into()
213    }
214
215    /// Shift left `buffer` by `rhs` bits.
216    fn shl_large(mut buffer: Buffer, rhs: usize) -> UBig {
217        let shift_words = rhs / WORD_BITS_USIZE;
218
219        if buffer.capacity() < buffer.len() + shift_words + 1 {
220            return UBig::shl_ref_large(&buffer, rhs);
221        }
222
223        let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
224        let carry = shift::shl_in_place(&mut buffer, shift_bits);
225        buffer.push(carry);
226        buffer.push_zeros_front(shift_words);
227        buffer.into()
228    }
229
230    /// Shift left large number of words by `rhs` bits.
231    fn shl_ref_large(words: &[Word], rhs: usize) -> UBig {
232        let shift_words = rhs / WORD_BITS_USIZE;
233        let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
234
235        let mut buffer = Buffer::allocate(shift_words + words.len() + 1);
236        buffer.push_zeros(shift_words);
237        buffer.extend(words);
238        let carry = shift::shl_in_place(&mut buffer[shift_words..], shift_bits);
239        buffer.push(carry);
240        buffer.into()
241    }
242
243    /// Shift right one `Word` by `rhs` bits.
244    #[inline]
245    fn shr_word(word: Word, rhs: usize) -> UBig {
246        let word = if rhs < WORD_BITS_USIZE {
247            word >> rhs
248        } else {
249            0
250        };
251        UBig::from_word(word)
252    }
253
254    /// Shift right `buffer` by `rhs` bits.
255    fn shr_large(mut buffer: Buffer, rhs: usize) -> UBig {
256        let shift_words = rhs / WORD_BITS_USIZE;
257        if shift_words >= buffer.len() {
258            return UBig::from_word(0);
259        }
260        let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
261        buffer.erase_front(shift_words);
262        shift::shr_in_place(&mut buffer, shift_bits);
263        buffer.into()
264    }
265
266    /// Shift right large number of words by `rhs` bits.
267    fn shr_large_ref(words: &[Word], rhs: usize) -> UBig {
268        let shift_words = rhs / WORD_BITS_USIZE;
269        let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
270
271        let words = &words[shift_words.min(words.len())..];
272
273        match words {
274            [] => UBig::from_word(0),
275            &[w] => UBig::from_word(w >> shift_bits),
276            &[lo, hi] => UBig::from(double_word(lo, hi) >> shift_bits),
277            _ => {
278                let mut buffer = Buffer::allocate(words.len());
279                buffer.extend(words);
280                shift::shr_in_place(&mut buffer, shift_bits);
281                buffer.into()
282            }
283        }
284    }
285}