use crate::integer::Integer;
use crate::integer::conversion::to_twos_complement_limbs::limbs_twos_complement_in_place;
use crate::natural::InnerNatural::{Large, Small};
use crate::natural::arithmetic::add::limbs_vec_add_limb_in_place;
use crate::natural::arithmetic::mod_power_of_2::limbs_vec_mod_power_of_2_in_place;
use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
use crate::natural::arithmetic::sub::limbs_sub_limb_in_place;
use crate::natural::logic::bit_block_access::limbs_assign_bits_helper;
use crate::natural::logic::not::limbs_not_in_place;
use crate::natural::logic::trailing_zeros::limbs_trailing_zeros;
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, TrailingZeros};
use malachite_base::vecs::vec_delete_left;
pub_test! {limbs_neg_limb_get_bits(x: Limb, start: u64, end: u64) -> Vec<Limb> {
assert!(start <= end);
let trailing_zeros = TrailingZeros::trailing_zeros(x);
if trailing_zeros >= end {
return Vec::new();
}
let bit_len = end - start;
let mut out = if start >= Limb::WIDTH {
vec![
Limb::MAX;
bit_to_limb_count_ceiling(bit_len)
]
} else {
let mut out = vec![x >> start];
out.resize(bit_to_limb_count_floor(end) + 1, 0);
if trailing_zeros >= start {
limbs_twos_complement_in_place(&mut out);
} else {
limbs_not_in_place(&mut out);
}
out
};
limbs_vec_mod_power_of_2_in_place(&mut out, bit_len);
out
}}
pub_test! {limbs_slice_neg_get_bits(xs: &[Limb], start: u64, end: u64) -> Vec<Limb> {
assert!(start <= end);
let trailing_zeros = limbs_trailing_zeros(xs);
if trailing_zeros >= end {
return Vec::new();
}
let start_i = bit_to_limb_count_floor(start);
let len = xs.len();
let bit_len = end - start;
if start_i >= len {
let mut out = vec![Limb::MAX; bit_to_limb_count_ceiling(bit_len)];
limbs_vec_mod_power_of_2_in_place(&mut out, bit_len);
return out;
}
let end_i = bit_to_limb_count_floor(end) + 1;
let mut out = (if end_i >= len {
&xs[start_i..]
} else {
&xs[start_i..end_i]
})
.to_vec();
let offset = start & Limb::WIDTH_MASK;
if offset != 0 {
limbs_slice_shr_in_place(&mut out, offset);
}
out.resize(end_i - start_i, 0);
if trailing_zeros >= start {
limbs_twos_complement_in_place(&mut out);
} else {
limbs_not_in_place(&mut out);
}
limbs_vec_mod_power_of_2_in_place(&mut out, bit_len);
out
}}
pub_test! {limbs_vec_neg_get_bits(mut xs: Vec<Limb>, start: u64, end: u64) -> Vec<Limb> {
assert!(start <= end);
let trailing_zeros = limbs_trailing_zeros(&xs);
if trailing_zeros >= end {
return Vec::new();
}
let start_i = bit_to_limb_count_floor(start);
let len = xs.len();
let bit_len = end - start;
if start_i >= len {
xs = vec![Limb::MAX; bit_to_limb_count_ceiling(bit_len)];
limbs_vec_mod_power_of_2_in_place(&mut xs, bit_len);
return xs;
}
let end_i = bit_to_limb_count_floor(end) + 1;
xs.truncate(end_i);
vec_delete_left(&mut xs, start_i);
let offset = start & Limb::WIDTH_MASK;
if offset != 0 {
limbs_slice_shr_in_place(&mut xs, offset);
}
xs.resize(end_i - start_i, 0);
if trailing_zeros >= start {
limbs_twos_complement_in_place(&mut xs);
} else {
limbs_not_in_place(&mut xs);
}
limbs_vec_mod_power_of_2_in_place(&mut xs, bit_len);
xs
}}
pub_test! {limbs_neg_assign_bits(xs: &mut Vec<Limb>, start: u64, end: u64, bits: &[Limb]) {
assert!(start < end);
assert!(!limbs_sub_limb_in_place(xs, 1));
limbs_assign_bits_helper(xs, start, end, bits, true);
limbs_vec_add_limb_in_place(xs, 1);
}}
impl Natural {
fn neg_get_bits(&self, start: u64, end: u64) -> Self {
Self::from_owned_limbs_asc(match self {
Self(Small(small)) => limbs_neg_limb_get_bits(*small, start, end),
Self(Large(limbs)) => limbs_slice_neg_get_bits(limbs, start, end),
})
}
fn neg_get_bits_owned(self, start: u64, end: u64) -> Self {
Self::from_owned_limbs_asc(match self {
Self(Small(small)) => limbs_neg_limb_get_bits(small, start, end),
Self(Large(limbs)) => limbs_vec_neg_get_bits(limbs, start, end),
})
}
fn neg_assign_bits(&mut self, start: u64, end: u64, bits: &Self) {
if start == end {
return;
}
let bits_width = end - start;
if bits_width <= Limb::WIDTH
&& let (&mut Self(Small(ref mut small_self)), &Self(Small(small_bits))) =
(&mut *self, bits)
{
let small_bits = (!small_bits).mod_power_of_2(bits_width);
if small_bits == 0 || LeadingZeros::leading_zeros(small_bits) >= start {
let mut new_small_self = *small_self - 1;
new_small_self.assign_bits(start, end, &small_bits);
let (sum, overflow) = new_small_self.overflowing_add(1);
if !overflow {
*small_self = sum;
return;
}
}
}
let limbs = self.promote_in_place();
match bits {
Self(Small(small_bits)) => limbs_neg_assign_bits(limbs, start, end, &[*small_bits]),
Self(Large(bits_limbs)) => limbs_neg_assign_bits(limbs, start, end, bits_limbs),
}
self.trim();
}
}
impl BitBlockAccess for Integer {
type Bits = Natural;
fn get_bits(&self, start: u64, end: u64) -> Natural {
if self.sign {
self.abs.get_bits(start, end)
} else {
self.abs.neg_get_bits(start, end)
}
}
fn get_bits_owned(self, start: u64, end: u64) -> Natural {
if self.sign {
self.abs.get_bits_owned(start, end)
} else {
self.abs.neg_get_bits_owned(start, end)
}
}
fn assign_bits(&mut self, start: u64, end: u64, bits: &Natural) {
if self.sign {
self.abs.assign_bits(start, end, bits);
} else {
self.abs.neg_assign_bits(start, end, bits);
}
}
}