use crate::natural::InnerNatural::{Large, Small};
use crate::natural::{Natural, bit_to_limb_count_floor};
use crate::platform::Limb;
use alloc::vec::Vec;
use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
use malachite_base::num::arithmetic::traits::UnsignedAbs;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::signeds::PrimitiveSigned;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::{ExactFrom, WrappingFrom};
use malachite_base::vecs::vec_delete_left;
pub_crate_test! {limbs_shr(xs: &[Limb], bits: u64) -> Vec<Limb> {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
Vec::new()
} else {
let small_bits = bits & Limb::WIDTH_MASK;
let src = &xs[delete_count..];
if small_bits == 0 {
src.to_vec()
} else {
let cobits = Limb::WIDTH - small_bits;
let mut out = Vec::with_capacity(src.len());
out.extend(
src.windows(2)
.map(|w| (w[0] >> small_bits) | (w[1] << cobits)),
);
out.push(src[src.len() - 1] >> small_bits);
out
}
}
}}
pub_crate_test! {limbs_shr_to_out(out: &mut [Limb], xs: &[Limb], bits: u64) -> Limb {
let len = xs.len();
assert_ne!(len, 0);
assert_ne!(bits, 0);
assert!(bits < Limb::WIDTH);
assert!(out.len() >= len);
let cobits = Limb::WIDTH - bits;
let remaining_bits = xs[0] << cobits;
for (o, w) in out[..len - 1].iter_mut().zip(xs.windows(2)) {
*o = (w[0] >> bits) | (w[1] << cobits);
}
out[len - 1] = xs[len - 1] >> bits;
remaining_bits
}}
pub_crate_test! {limbs_slice_shr_in_place<T: PrimitiveUnsigned>(xs: &mut [T], bits: u64) -> T {
assert_ne!(bits, 0);
assert!(bits < T::WIDTH);
let len = xs.len();
assert_ne!(len, 0);
let cobits = T::WIDTH - bits;
let remaining_bits = xs[0] << cobits;
for i in 0..len - 1 {
xs[i] = (xs[i] >> bits) | (xs[i + 1] << cobits);
}
xs[len - 1] >>= bits;
remaining_bits
}}
pub_crate_test! {limbs_vec_shr_in_place(xs: &mut Vec<Limb>, bits: u64) {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
xs.clear();
} else {
let small_shift = bits & Limb::WIDTH_MASK;
if small_shift == 0 {
vec_delete_left(xs, delete_count);
} else {
let old_len = xs.len();
let new_len = old_len - delete_count;
let cobits = Limb::WIDTH - small_shift;
for i in 0..new_len - 1 {
xs[i] = (xs[i + delete_count] >> small_shift)
| (xs[i + delete_count + 1] << cobits);
}
xs[new_len - 1] = xs[old_len - 1] >> small_shift;
xs.truncate(new_len);
}
}
}}
fn shr_unsigned_ref<T: Copy + Eq + Ord + WrappingFrom<u64> + Zero>(x: &Natural, bits: T) -> Natural
where
u64: ExactFrom<T>,
Limb: Shr<T, Output = Limb>,
{
match (x, bits) {
(&Natural::ZERO, _) => x.clone(),
(_, bits) if bits == T::ZERO => x.clone(),
(Natural(Small(_)), bits) if bits >= T::wrapping_from(Limb::WIDTH) => Natural::ZERO,
(Natural(Small(small)), bits) => Natural(Small(*small >> bits)),
(Natural(Large(limbs)), bits) => {
Natural::from_owned_limbs_asc(limbs_shr(limbs, u64::exact_from(bits)))
}
}
}
fn shr_assign_unsigned<T: PrimitiveUnsigned>(x: &mut Natural, bits: T)
where
u64: ExactFrom<T>,
Limb: ShrAssign<T>,
{
match (&mut *x, bits) {
(&mut Natural::ZERO, _) => {}
(_, bits) if bits == T::ZERO => {}
(Natural(Small(small)), bits) if bits >= T::wrapping_from(Limb::WIDTH) => {
*small = 0;
}
(Natural(Small(small)), bits) => {
*small >>= bits;
}
(Natural(Large(limbs)), bits) => {
limbs_vec_shr_in_place(limbs, u64::exact_from(bits));
x.trim();
}
}
}
macro_rules! impl_natural_shr_unsigned {
($t:ident) => {
impl Shr<$t> for Natural {
type Output = Natural;
#[inline]
fn shr(mut self, bits: $t) -> Natural {
self >>= bits;
self
}
}
impl<'a> Shr<$t> for &Natural {
type Output = Natural;
#[inline]
fn shr(self, bits: $t) -> Natural {
shr_unsigned_ref(self, bits)
}
}
impl ShrAssign<$t> for Natural {
#[inline]
fn shr_assign(&mut self, bits: $t) {
shr_assign_unsigned(self, bits);
}
}
};
}
apply_to_unsigneds!(impl_natural_shr_unsigned);
fn shr_signed_ref<'a, U, S: PrimitiveSigned + UnsignedAbs<Output = U>>(
x: &'a Natural,
bits: S,
) -> Natural
where
&'a Natural: Shl<U, Output = Natural> + Shr<U, Output = Natural>,
{
if bits >= S::ZERO {
x >> bits.unsigned_abs()
} else {
x << bits.unsigned_abs()
}
}
fn shr_assign_signed<U, S: PrimitiveSigned + UnsignedAbs<Output = U>>(x: &mut Natural, bits: S)
where
Natural: ShlAssign<U> + ShrAssign<U>,
{
if bits >= S::ZERO {
*x >>= bits.unsigned_abs();
} else {
*x <<= bits.unsigned_abs();
}
}
macro_rules! impl_natural_shr_signed {
($t:ident) => {
impl Shr<$t> for Natural {
type Output = Natural;
#[inline]
fn shr(mut self, bits: $t) -> Natural {
self >>= bits;
self
}
}
impl<'a> Shr<$t> for &Natural {
type Output = Natural;
#[inline]
fn shr(self, bits: $t) -> Natural {
shr_signed_ref(self, bits)
}
}
impl ShrAssign<$t> for Natural {
#[inline]
fn shr_assign(&mut self, bits: $t) {
shr_assign_signed(self, bits);
}
}
};
}
apply_to_signeds!(impl_natural_shr_signed);