| use super::{biguint_from_vec, BigUint}; |
| |
| use crate::big_digit; |
| |
| use alloc::borrow::Cow; |
| use alloc::vec::Vec; |
| use core::mem; |
| use core::ops::{Shl, ShlAssign, Shr, ShrAssign}; |
| use num_traits::{PrimInt, Zero}; |
| |
| #[inline] |
| fn biguint_shl<T: PrimInt>(n: Cow<'_, BigUint>, shift: T) -> BigUint { |
| if shift < T::zero() { |
| panic!("attempt to shift left with negative"); |
| } |
| if n.is_zero() { |
| return n.into_owned(); |
| } |
| let bits = T::from(big_digit::BITS).unwrap(); |
| let digits = (shift / bits).to_usize().expect("capacity overflow"); |
| let shift = (shift % bits).to_u8().unwrap(); |
| biguint_shl2(n, digits, shift) |
| } |
| |
| fn biguint_shl2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { |
| let mut data = match digits { |
| 0 => n.into_owned().data, |
| _ => { |
| let len = digits.saturating_add(n.data.len() + 1); |
| let mut data = Vec::with_capacity(len); |
| data.resize(digits, 0); |
| data.extend(n.data.iter()); |
| data |
| } |
| }; |
| |
| if shift > 0 { |
| let mut carry = 0; |
| let carry_shift = big_digit::BITS - shift; |
| for elem in data[digits..].iter_mut() { |
| let new_carry = *elem >> carry_shift; |
| *elem = (*elem << shift) | carry; |
| carry = new_carry; |
| } |
| if carry != 0 { |
| data.push(carry); |
| } |
| } |
| |
| biguint_from_vec(data) |
| } |
| |
| #[inline] |
| fn biguint_shr<T: PrimInt>(n: Cow<'_, BigUint>, shift: T) -> BigUint { |
| if shift < T::zero() { |
| panic!("attempt to shift right with negative"); |
| } |
| if n.is_zero() { |
| return n.into_owned(); |
| } |
| let bits = T::from(big_digit::BITS).unwrap(); |
| let digits = (shift / bits).to_usize().unwrap_or(usize::MAX); |
| let shift = (shift % bits).to_u8().unwrap(); |
| biguint_shr2(n, digits, shift) |
| } |
| |
| fn biguint_shr2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { |
| if digits >= n.data.len() { |
| let mut n = n.into_owned(); |
| n.set_zero(); |
| return n; |
| } |
| let mut data = match n { |
| Cow::Borrowed(n) => n.data[digits..].to_vec(), |
| Cow::Owned(mut n) => { |
| n.data.drain(..digits); |
| n.data |
| } |
| }; |
| |
| if shift > 0 { |
| let mut borrow = 0; |
| let borrow_shift = big_digit::BITS - shift; |
| for elem in data.iter_mut().rev() { |
| let new_borrow = *elem << borrow_shift; |
| *elem = (*elem >> shift) | borrow; |
| borrow = new_borrow; |
| } |
| } |
| |
| biguint_from_vec(data) |
| } |
| |
| macro_rules! impl_shift { |
| (@ref $Shx:ident :: $shx:ident, $ShxAssign:ident :: $shx_assign:ident, $rhs:ty) => { |
| impl $Shx<&$rhs> for BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn $shx(self, rhs: &$rhs) -> BigUint { |
| $Shx::$shx(self, *rhs) |
| } |
| } |
| impl $Shx<&$rhs> for &BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn $shx(self, rhs: &$rhs) -> BigUint { |
| $Shx::$shx(self, *rhs) |
| } |
| } |
| impl $ShxAssign<&$rhs> for BigUint { |
| #[inline] |
| fn $shx_assign(&mut self, rhs: &$rhs) { |
| $ShxAssign::$shx_assign(self, *rhs); |
| } |
| } |
| }; |
| ($($rhs:ty),+) => {$( |
| impl Shl<$rhs> for BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn shl(self, rhs: $rhs) -> BigUint { |
| biguint_shl(Cow::Owned(self), rhs) |
| } |
| } |
| impl Shl<$rhs> for &BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn shl(self, rhs: $rhs) -> BigUint { |
| biguint_shl(Cow::Borrowed(self), rhs) |
| } |
| } |
| impl ShlAssign<$rhs> for BigUint { |
| #[inline] |
| fn shl_assign(&mut self, rhs: $rhs) { |
| let n = mem::replace(self, Self::ZERO); |
| *self = n << rhs; |
| } |
| } |
| impl_shift! { @ref Shl::shl, ShlAssign::shl_assign, $rhs } |
| |
| impl Shr<$rhs> for BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn shr(self, rhs: $rhs) -> BigUint { |
| biguint_shr(Cow::Owned(self), rhs) |
| } |
| } |
| impl Shr<$rhs> for &BigUint { |
| type Output = BigUint; |
| |
| #[inline] |
| fn shr(self, rhs: $rhs) -> BigUint { |
| biguint_shr(Cow::Borrowed(self), rhs) |
| } |
| } |
| impl ShrAssign<$rhs> for BigUint { |
| #[inline] |
| fn shr_assign(&mut self, rhs: $rhs) { |
| let n = mem::replace(self, Self::ZERO); |
| *self = n >> rhs; |
| } |
| } |
| impl_shift! { @ref Shr::shr, ShrAssign::shr_assign, $rhs } |
| )*}; |
| } |
| |
| impl_shift! { u8, u16, u32, u64, u128, usize } |
| impl_shift! { i8, i16, i32, i64, i128, isize } |