use crate::integer::Integer;
use crate::integer_polynomial::IntegerPolynomial;
use crate::integer_polynomial::arithmetic::bit_pack::field_start;
use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
use crate::integer_polynomial::arithmetic::vec::SMALL_FMPZ_BITCOUNT_MAX;
use crate::natural::Natural;
use crate::natural::arithmetic::add::limbs_slice_add_limb_in_place;
use crate::natural::arithmetic::shr::limbs_shr_to_out;
use crate::natural::logic::not::limbs_not_in_place;
use crate::platform::{Limb, SignedLimb};
use alloc::vec;
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::{
ModPowerOf2Assign, NegAssign, PowerOf2, WrappingAddAssign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::One;
use malachite_base::num::conversion::traits::{ExactFrom, PowerOf2Digits, WrappingFrom};
use malachite_base::num::logic::traits::{BitAccess, LowMask};
use malachite_base::polynomial::{BitUnpack, Polynomial};
fn limbs_extract_field(arr: &[Limb], shift: u64, bits: u64) -> Vec<Limb> {
let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
let l = usize::exact_from(bits.div_ceil(Limb::WIDTH));
let b = bits & Limb::WIDTH_MASK;
let mut p = vec![0; l];
if shift != 0 {
limbs_shr_to_out(&mut p, &arr[..l], shift);
} else {
p.copy_from_slice(&arr[..l]);
}
if limbs + usize::from(rem_bits != 0) > l {
p[l - 1].wrapping_add_assign(arr[limbs] << (Limb::WIDTH - shift));
}
if b != 0 {
p[l - 1].mod_power_of_2_assign(b);
}
p
}
crate_test_fn! {limbs_unpack_field<C: PolynomialCoefficient>(
arr: &[Limb],
shift: u64,
bits: u64,
negate: bool,
borrow: bool,
) -> (C, bool) {
let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
let sign = if rem_bits != 0 {
arr[limbs].get_bit(rem_bits - 1)
} else {
arr[limbs - 1].get_highest_bit()
};
let (value_sign, abs, negative) = if bits <= SMALL_FMPZ_BITCOUNT_MAX {
let mask = Limb::low_mask(bits);
let mut c = if limbs + usize::from(rem_bits != 0) > 1 {
((arr[0] >> shift).wrapping_add(arr[1] << (Limb::WIDTH - shift))) & mask
} else {
(arr[0] >> shift) & mask
};
if sign {
c.wrapping_add_assign(Limb::MAX << bits);
}
let c = SignedLimb::wrapping_from(c);
let value = c + SignedLimb::from(borrow);
(
value >= 0,
Natural::from(Limb::wrapping_from(value.unsigned_abs())),
c < 0,
)
} else {
let mut p = limbs_extract_field(arr, shift, bits);
let l = p.len();
let b = bits & Limb::WIDTH_MASK;
if sign {
if b != 0 {
p[l - 1].wrapping_add_assign(Limb::MAX << b);
}
limbs_not_in_place(&mut p);
if !borrow {
limbs_slice_add_limb_in_place(&mut p, 1);
}
(false, Natural::from_owned_limbs_asc(p), true)
} else {
if borrow {
limbs_slice_add_limb_in_place(&mut p, 1);
}
(true, Natural::from_owned_limbs_asc(p), false)
}
};
(C::from_sign_and_abs(value_sign != negate, abs), negative)
}}
crate_test_fn! {limbs_unpack_field_unsigned<C: PolynomialCoefficient>(
arr: &[Limb],
shift: u64,
bits: u64,
) -> C {
let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
if bits <= SMALL_FMPZ_BITCOUNT_MAX {
let mask = Limb::low_mask(bits);
C::from_sign_and_abs(true, Natural::from(if limbs + usize::from(rem_bits != 0) > 1 {
((arr[0] >> shift).wrapping_add(arr[1] << (Limb::WIDTH - shift))) & mask
} else {
(arr[0] >> shift) & mask
}))
} else {
C::from_sign_and_abs(
true,
Natural::from_owned_limbs_asc(limbs_extract_field(arr, shift, bits)),
)
}
}}
crate_test_fn! {limbs_unpack_coefficients<C: PolynomialCoefficient>(
out: &mut [C],
nlo: usize,
nhi: usize,
xs: &[Limb],
bits: u64,
negate: bool,
) -> bool {
let mut borrow = if nlo == 0 {
false
} else {
let top = u64::exact_from(nlo) * bits - 1;
xs[usize::exact_from(top >> Limb::LOG_WIDTH)].get_bit(top & Limb::WIDTH_MASK)
};
for (c, i) in out.iter_mut().zip(nlo..nhi) {
let (limbs, shift) = field_start(i, bits);
let next_borrow;
(*c, next_borrow) = limbs_unpack_field(&xs[limbs..], shift, bits, negate, borrow);
borrow = next_borrow;
}
borrow
}}
crate_test_fn! {limbs_unpack_coefficients_unsigned<C: PolynomialCoefficient>(
out: &mut [C],
nlo: usize,
nhi: usize,
xs: &[Limb],
bits: u64,
) {
for (c, i) in out.iter_mut().zip(nlo..nhi) {
let (limbs, shift) = field_start(i, bits);
*c = limbs_unpack_field_unsigned(&xs[limbs..], shift, bits);
}
}}
fn bit_unpack_ref(n: &Integer, bits: u64) -> IntegerPolynomial {
assert_ne!(bits, 0, "Cannot unpack a polynomial from fields of 0 bits");
let digits: Vec<Natural> = n.unsigned_abs_ref().to_power_of_2_digits_asc(bits);
let field = Integer::power_of_2(bits);
let mut coefficients = Vec::with_capacity(digits.len() + 1);
let mut borrow = false;
for digit in digits {
let negative = digit.get_bit(bits - 1);
let mut c = Integer::from(digit);
if negative {
c -= &field;
}
if borrow {
c += Integer::ONE;
}
coefficients.push(c);
borrow = negative;
}
if borrow {
coefficients.push(Integer::ONE);
}
if *n < 0u32 {
for c in &mut coefficients {
c.neg_assign();
}
}
IntegerPolynomial::from_coefficients_asc(coefficients)
}
impl BitUnpack<Integer> for IntegerPolynomial {
#[inline]
fn bit_unpack(n: Integer, bits: u64) -> Self {
bit_unpack_ref(&n, bits)
}
}
impl BitUnpack<&Integer> for IntegerPolynomial {
#[inline]
fn bit_unpack(n: &Integer, bits: u64) -> Self {
bit_unpack_ref(n, bits)
}
}