num_bigint/biguint/
shift.rs1use super::BigUint;
2
3use crate::big_digit::{self, BigDigits};
4
5use alloc::borrow::Cow;
6use alloc::vec::Vec;
7use core::mem;
8use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
9use num_traits::{PrimInt, Zero};
10
11#[inline]
12pub(super) fn biguint_shl<T: PrimInt>(n: Cow<'_, BigUint>, shift: T) -> BigUint {
13 if shift < T::zero() {
14 panic!("attempt to shift left with negative");
15 }
16 if n.is_zero() {
17 return n.into_owned();
18 }
19 let bits = T::from(big_digit::BITS).unwrap();
20 let digits = (shift / bits).to_usize().expect("capacity overflow");
21 let shift = (shift % bits).to_u8().unwrap();
22 biguint_shl2(n, digits, shift)
23}
24
25fn biguint_shl2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint {
26 let mut data = match digits {
27 0 => n.into_owned().data,
28 _ => {
29 let len = digits.saturating_add(n.data.len() + 1);
30 let mut data = Vec::with_capacity(len);
31 data.resize(digits, 0);
32 data.extend(n.data.iter());
33 BigDigits::from_vec(data)
34 }
35 };
36
37 if shift > 0 {
38 let mut carry = 0;
39 let carry_shift = big_digit::BITS - shift;
40 for elem in data[digits..].iter_mut() {
41 let new_carry = *elem >> carry_shift;
42 *elem = (*elem << shift) | carry;
43 carry = new_carry;
44 }
45 if carry != 0 {
46 data.push(carry);
47 }
48 }
49
50 BigUint { data }
51}
52
53#[inline]
54fn biguint_shr<T: PrimInt>(n: Cow<'_, BigUint>, shift: T) -> BigUint {
55 if shift < T::zero() {
56 panic!("attempt to shift right with negative");
57 }
58 if n.is_zero() {
59 return n.into_owned();
60 }
61 let bits = T::from(big_digit::BITS).unwrap();
62 let digits = (shift / bits).to_usize().unwrap_or(usize::MAX);
63 let shift = (shift % bits).to_u8().unwrap();
64 biguint_shr2(n, digits, shift)
65}
66
67fn biguint_shr2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint {
68 if digits >= n.data.len() {
69 return match n {
70 Cow::Borrowed(_) => BigUint::ZERO,
71 Cow::Owned(mut n) => {
72 n.set_zero();
73 n
74 }
75 };
76 }
77 let mut data = match n {
78 Cow::Borrowed(n) => BigDigits::from_slice(&n.data[digits..]),
79 Cow::Owned(mut n) => {
80 n.data.drain_front(digits);
81 n.data.shrink();
82 n.data
83 }
84 };
85
86 if shift > 0 {
87 let mut borrow = 0;
88 let borrow_shift = big_digit::BITS - shift;
89 for elem in data.iter_mut().rev() {
90 let new_borrow = *elem << borrow_shift;
91 *elem = (*elem >> shift) | borrow;
92 borrow = new_borrow;
93 }
94 if !data.is_normal() {
97 data.pop();
98 }
99 }
100
101 BigUint { data }
102}
103
104macro_rules! impl_shift {
105 (@ref $Shx:ident :: $shx:ident, $ShxAssign:ident :: $shx_assign:ident, $rhs:ty) => {
106 impl $Shx<&$rhs> for BigUint {
107 type Output = BigUint;
108
109 #[inline]
110 fn $shx(self, rhs: &$rhs) -> BigUint {
111 $Shx::$shx(self, *rhs)
112 }
113 }
114 impl $Shx<&$rhs> for &BigUint {
115 type Output = BigUint;
116
117 #[inline]
118 fn $shx(self, rhs: &$rhs) -> BigUint {
119 $Shx::$shx(self, *rhs)
120 }
121 }
122 impl $ShxAssign<&$rhs> for BigUint {
123 #[inline]
124 fn $shx_assign(&mut self, rhs: &$rhs) {
125 $ShxAssign::$shx_assign(self, *rhs);
126 }
127 }
128 };
129 ($($rhs:ty),+) => {$(
130 impl Shl<$rhs> for BigUint {
131 type Output = BigUint;
132
133 #[inline]
134 fn shl(self, rhs: $rhs) -> BigUint {
135 biguint_shl(Cow::Owned(self), rhs)
136 }
137 }
138 impl Shl<$rhs> for &BigUint {
139 type Output = BigUint;
140
141 #[inline]
142 fn shl(self, rhs: $rhs) -> BigUint {
143 biguint_shl(Cow::Borrowed(self), rhs)
144 }
145 }
146 impl ShlAssign<$rhs> for BigUint {
147 #[inline]
148 fn shl_assign(&mut self, rhs: $rhs) {
149 let n = mem::replace(self, Self::ZERO);
150 *self = n << rhs;
151 }
152 }
153 impl_shift! { @ref Shl::shl, ShlAssign::shl_assign, $rhs }
154
155 impl Shr<$rhs> for BigUint {
156 type Output = BigUint;
157
158 #[inline]
159 fn shr(self, rhs: $rhs) -> BigUint {
160 biguint_shr(Cow::Owned(self), rhs)
161 }
162 }
163 impl Shr<$rhs> for &BigUint {
164 type Output = BigUint;
165
166 #[inline]
167 fn shr(self, rhs: $rhs) -> BigUint {
168 biguint_shr(Cow::Borrowed(self), rhs)
169 }
170 }
171 impl ShrAssign<$rhs> for BigUint {
172 #[inline]
173 fn shr_assign(&mut self, rhs: $rhs) {
174 let n = mem::replace(self, Self::ZERO);
175 *self = n >> rhs;
176 }
177 }
178 impl_shift! { @ref Shr::shr, ShrAssign::shr_assign, $rhs }
179 )*};
180}
181
182impl_shift! { u8, u16, u32, u64, u128, usize }
183impl_shift! { i8, i16, i32, i64, i128, isize }