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::{ArithmeticCheckedShl, 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;
use malachite_base::vecs::vec_pad_left;
pub_crate_test! {limbs_shl(xs: &[Limb], bits: u64) -> Vec<Limb> {
let small_bits = bits & Limb::WIDTH_MASK;
let limb_offset = bit_to_limb_count_floor(bits);
let xs_len = xs.len();
if small_bits == 0 {
let mut out = vec![0; limb_offset + xs_len];
out[limb_offset..].copy_from_slice(xs);
out
} else if xs_len == 0 {
vec![0; limb_offset]
} else {
let cobits = Limb::WIDTH - small_bits;
let mut out = Vec::with_capacity(limb_offset + xs_len + 1);
out.resize(limb_offset, 0);
out.push(xs[0] << small_bits);
out.extend(
xs.windows(2)
.map(|w| (w[1] << small_bits) | (w[0] >> cobits)),
);
let remaining_bits = xs[xs_len - 1] >> cobits;
if remaining_bits != 0 {
out.push(remaining_bits);
}
out
}
}}
pub_crate_test! {limbs_shl_to_out(out: &mut [Limb], xs: &[Limb], bits: u64) -> Limb {
assert_ne!(bits, 0);
assert!(bits < Limb::WIDTH);
let len = xs.len();
if len == 0 {
return 0;
}
let cobits = Limb::WIDTH - bits;
out[0] = xs[0] << bits;
for (o, w) in out[1..len].iter_mut().zip(xs.windows(2)) {
*o = (w[1] << bits) | (w[0] >> cobits);
}
xs[len - 1] >> cobits
}}
pub_crate_test! {limbs_slice_shl_in_place(xs: &mut [Limb], bits: u64) -> Limb {
assert_ne!(bits, 0);
assert!(bits < Limb::WIDTH);
let len = xs.len();
if len == 0 {
return 0;
}
let cobits = Limb::WIDTH - bits;
let remaining_bits = xs[len - 1] >> cobits;
for i in (1..len).rev() {
xs[i] = (xs[i] << bits) | (xs[i - 1] >> cobits);
}
xs[0] <<= bits;
remaining_bits
}}
pub_crate_test! {limbs_vec_shl_in_place(xs: &mut Vec<Limb>, bits: u64) {
let small_bits = bits & Limb::WIDTH_MASK;
let limb_offset = bit_to_limb_count_floor(bits);
if small_bits == 0 {
vec_pad_left(xs, limb_offset, 0);
return;
}
let old_len = xs.len();
if old_len == 0 {
xs.resize(limb_offset, 0);
return;
}
let cobits = Limb::WIDTH - small_bits;
xs.resize(old_len + limb_offset + 1, 0);
let remaining_bits = xs[old_len - 1] >> cobits;
xs[old_len + limb_offset] = remaining_bits;
for i in (1..old_len).rev() {
xs[i + limb_offset] = (xs[i] << small_bits) | (xs[i - 1] >> cobits);
}
xs[limb_offset] = xs[0] << small_bits;
xs[..limb_offset].fill(0);
if remaining_bits == 0 {
xs.pop();
}
}}
pub_crate_test! {limbs_shl_with_complement_to_out(
out: &mut [Limb],
xs: &[Limb],
bits: u64
) -> Limb {
let n = xs.len();
assert_ne!(n, 0);
assert_ne!(bits, 0);
assert!(bits < Limb::WIDTH);
let cobits = Limb::WIDTH - bits;
out[0] = !(xs[0] << bits);
for (o, w) in out[1..n].iter_mut().zip(xs.windows(2)) {
*o = !((w[1] << bits) | (w[0] >> cobits));
}
xs[n - 1] >> cobits
}}
fn shl_ref_unsigned<T: PrimitiveUnsigned>(x: &Natural, bits: T) -> Natural
where
u64: ExactFrom<T>,
Limb: ArithmeticCheckedShl<T, Output = Limb>,
{
match (x, bits) {
(&Natural::ZERO, _) => x.clone(),
(_, bits) if bits == T::ZERO => x.clone(),
(Natural(Small(small)), bits) => {
Natural(if let Some(shifted) = small.arithmetic_checked_shl(bits) {
Small(shifted)
} else {
Large(limbs_shl(&[*small], u64::exact_from(bits)))
})
}
(Natural(Large(limbs)), bits) => Natural(Large(limbs_shl(limbs, u64::exact_from(bits)))),
}
}
fn shl_assign<T: PrimitiveUnsigned>(x: &mut Natural, bits: T)
where
u64: ExactFrom<T>,
Limb: ArithmeticCheckedShl<T, Output = Limb>,
{
match (&mut *x, bits) {
(&mut Natural::ZERO, _) => {}
(_, bits) if bits == T::ZERO => {}
(Natural(Small(small)), bits) => {
if let Some(shifted) = small.arithmetic_checked_shl(bits) {
*small = shifted;
} else {
*x = Natural(Large(limbs_shl(&[*small], u64::exact_from(bits))));
}
}
(Natural(Large(limbs)), bits) => {
limbs_vec_shl_in_place(limbs, u64::exact_from(bits));
}
}
}
macro_rules! impl_natural_shl_unsigned {
($t:ident) => {
impl Shl<$t> for Natural {
type Output = Natural;
#[inline]
fn shl(mut self, bits: $t) -> Natural {
self <<= bits;
self
}
}
impl Shl<$t> for &Natural {
type Output = Natural;
#[inline]
fn shl(self, bits: $t) -> Natural {
shl_ref_unsigned(self, bits)
}
}
impl ShlAssign<$t> for Natural {
#[inline]
fn shl_assign(&mut self, bits: $t) {
shl_assign(self, bits);
}
}
};
}
apply_to_unsigneds!(impl_natural_shl_unsigned);
fn shl_ref_signed<'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 shl_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_shl_signed {
($t:ident) => {
impl Shl<$t> for Natural {
type Output = Natural;
#[inline]
fn shl(mut self, bits: $t) -> Natural {
self <<= bits;
self
}
}
impl<'a> Shl<$t> for &Natural {
type Output = Natural;
#[inline]
fn shl(self, bits: $t) -> Natural {
shl_ref_signed(self, bits)
}
}
impl ShlAssign<$t> for Natural {
#[inline]
fn shl_assign(&mut self, bits: $t) {
shl_assign_signed(self, bits);
}
}
};
}
apply_to_signeds!(impl_natural_shl_signed);