use super::{biguint_from_vec, BigUint}; use crate::big_digit; use crate::std_alloc::{Cow, Vec}; use core::mem; use core::ops::{Shl, ShlAssign, Shr, ShrAssign}; use num_traits::{PrimInt, Zero}; #[inline] fn biguint_shl(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(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(core::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, BigUint::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, BigUint::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 }