use crate::natural::InnerNatural::{Large, Small};
use crate::natural::arithmetic::mod_power_of_2::limbs_vec_mod_power_of_2_in_place;
use crate::natural::arithmetic::shl::limbs_slice_shl_in_place;
use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
use crate::natural::logic::not::limbs_not_in_place;
use crate::natural::{Natural, bit_to_limb_count_ceiling, bit_to_limb_count_floor};
use crate::platform::Limb;
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::ModPowerOf2;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::logic::traits::{BitBlockAccess, LeadingZeros};
use malachite_base::slices::slice_set_zero;
use malachite_base::vecs::vec_delete_left;
pub_crate_test! {limbs_slice_get_bits(xs: &[Limb], start: u64, end: u64) -> Vec<Limb> {
assert!(start <= end);
let small_start = bit_to_limb_count_floor(start);
let len = xs.len();
if small_start >= len {
return Vec::new();
}
let small_end = bit_to_limb_count_floor(end) + 1;
let mut out = (if small_end >= len {
&xs[small_start..]
} else {
&xs[small_start..small_end]
})
.to_vec();
let offset = start & Limb::WIDTH_MASK;
if offset != 0 {
limbs_slice_shr_in_place(&mut out, offset);
}
limbs_vec_mod_power_of_2_in_place(&mut out, end - start);
out
}}
pub_test! {limbs_vec_get_bits(mut xs: Vec<Limb>, start: u64, end: u64) -> Vec<Limb> {
assert!(start <= end);
let small_start = bit_to_limb_count_floor(start);
if small_start >= xs.len() {
return Vec::new();
}
limbs_vec_mod_power_of_2_in_place(&mut xs, end);
vec_delete_left(&mut xs, small_start);
let offset = start & Limb::WIDTH_MASK;
if offset != 0 {
limbs_slice_shr_in_place(&mut xs, offset);
}
xs
}}
fn copy_from_diff_len_slice(xs: &mut [Limb], ys: &[Limb]) {
let xs_len = xs.len();
let ys_len = ys.len();
if xs_len <= ys_len {
xs.copy_from_slice(&ys[..xs_len]);
} else {
let (xs_lo, xs_hi) = xs.split_at_mut(ys_len);
xs_lo.copy_from_slice(ys);
slice_set_zero(xs_hi);
}
}
pub(crate) fn limbs_assign_bits_helper(
xs: &mut Vec<Limb>,
start: u64,
end: u64,
mut bits: &[Limb],
invert: bool,
) {
let small_start = bit_to_limb_count_floor(start);
let small_end = bit_to_limb_count_floor(end - 1) + 1;
let width = bit_to_limb_count_ceiling(end - start);
if width < bits.len() {
bits = &bits[..width];
}
let start_remainder = start & Limb::WIDTH_MASK;
let end_remainder = end & Limb::WIDTH_MASK;
if small_end > xs.len() {
xs.resize(small_end, 0);
}
let out = &mut xs[small_start..small_end];
assert!(!out.is_empty());
let original_first = out[0];
let original_last = *out.last().unwrap();
copy_from_diff_len_slice(out, bits);
if invert {
limbs_not_in_place(out);
}
if start_remainder != 0 {
limbs_slice_shl_in_place(out, start_remainder);
out[0] |= original_first.mod_power_of_2(start_remainder);
}
if end_remainder != 0 {
out.last_mut().unwrap().assign_bits(
end_remainder,
Limb::WIDTH,
&(original_last >> end_remainder),
);
}
}
pub_test! {limbs_assign_bits(xs: &mut Vec<Limb>, start: u64, end: u64, bits: &[Limb]) {
assert!(start < end);
limbs_assign_bits_helper(xs, start, end, bits, false);
}}
impl BitBlockAccess for Natural {
type Bits = Self;
fn get_bits(&self, start: u64, end: u64) -> Self {
match self {
Self(Small(small)) => Self(Small(small.get_bits(start, end))),
Self(Large(limbs)) => {
Self::from_owned_limbs_asc(limbs_slice_get_bits(limbs, start, end))
}
}
}
fn get_bits_owned(self, start: u64, end: u64) -> Self {
match self {
Self(Small(small)) => Self(Small(small.get_bits(start, end))),
Self(Large(limbs)) => Self::from_owned_limbs_asc(limbs_vec_get_bits(limbs, start, end)),
}
}
fn assign_bits(&mut self, start: u64, end: u64, bits: &Self) {
if start == end {
return;
}
if let Self(Small(small_self)) = self
&& let Self(Small(small_bits)) = bits
{
let bits_width = end - start;
let small_bits = small_bits.mod_power_of_2(bits_width);
if small_bits == 0 || LeadingZeros::leading_zeros(small_bits) >= start {
small_self.assign_bits(start, end, &small_bits);
return;
}
}
let limbs = self.promote_in_place();
match bits {
Self(Small(small_bits)) => limbs_assign_bits(limbs, start, end, &[*small_bits]),
Self(Large(bits_limbs)) => limbs_assign_bits(limbs, start, end, bits_limbs),
}
self.trim();
}
}