use crate::natural::InnerNatural::Small;
use crate::natural::Natural;
use crate::natural::arithmetic::div_exact::limbs_modular_div_mod_wrap;
use crate::natural::arithmetic::square::{limbs_square_to_out, limbs_square_to_out_scratch_len};
use crate::natural::comparison::cmp::limbs_cmp_same_length;
use crate::platform::{LIMB_WIDTH_USIZE, Limb};
use alloc::vec::Vec;
use core::cmp::Ordering::Equal;
use core::cmp::min;
use core::mem::swap;
use malachite_base::num::arithmetic::traits::{Parity, Pow, PowerOf2};
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::factorization::traits::{RemovePower, RemovePowerAssign};
use malachite_base::num::logic::traits::{LowMask, SignificantBits};
use malachite_base::slices::slice_test_zero;
#[doc(hidden)]
pub fn limbs_remove(
wp: &mut Vec<Limb>, up: &[Limb], vp: &[Limb], cap: usize, ) -> usize {
let un = up.len();
let vn = vp.len();
assert!(un > 0);
assert!(vn > 0);
assert!(vp[0].odd(), "V must be odd for 2-adic division");
assert!(vn > 1 || vp[0] > 1, "V must be > 1 to avoid infinite loop");
let mut work = vec![0; ((un + 1) << 1) + ((un + 1 + vn) >> 1)];
let (mut qp, rest) = work.split_at_mut(un + 1);
let (mut qp2, tp) = rest.split_at_mut(un + 1);
qp[..un].copy_from_slice(up);
let mut qn = un;
let mut pwpsn = Vec::with_capacity(LIMB_WIDTH_USIZE);
let mut pwpsp_offsets = Vec::with_capacity(LIMB_WIDTH_USIZE);
let mut powers_storage = Vec::new();
let mut current_power_is_vp = true; let mut current_power_offset = 0; let mut pn = vn;
let mut npowers = 0;
while qn >= pn {
qp[qn] = 0;
if current_power_is_vp {
limbs_modular_div_mod_wrap(&mut qp2[..=qn - pn], &mut tp[..pn], &qp[..=qn], &vp[..pn]);
if !slice_test_zero(&tp[..pn]) && limbs_cmp_same_length(&tp[..pn], &vp[..pn]) != Equal {
break; }
} else {
let power_slice = &powers_storage[current_power_offset..current_power_offset + pn];
limbs_modular_div_mod_wrap(
&mut qp2[..=qn - pn],
&mut tp[..pn],
&qp[..=qn],
power_slice,
);
if !slice_test_zero(&tp[..pn]) && limbs_cmp_same_length(&tp[..pn], power_slice) != Equal
{
break; }
}
swap(&mut qp, &mut qp2);
qn -= pn;
if qp[qn] != 0 {
qn += 1;
}
pwpsp_offsets.push(if current_power_is_vp {
usize::MAX
} else {
current_power_offset
});
pwpsn.push(pn);
npowers += 1;
if ((2usize << npowers) - 1) > cap {
break;
}
let nn = (pn << 1) - 1;
if nn > qn {
break;
}
if npowers == 1 {
powers_storage = vec![0; qn + LIMB_WIDTH_USIZE];
}
let np_offset = if npowers == 1 {
0
} else {
powers_storage.len()
};
let np_end = np_offset + (pn << 1);
powers_storage.resize(np_end, 0);
let mut scratch = vec![0; limbs_square_to_out_scratch_len(pn)];
if current_power_is_vp {
limbs_square_to_out(
&mut powers_storage[np_offset..np_end],
&vp[..pn],
&mut scratch,
);
} else {
let (src_part, dst_part) = powers_storage.split_at_mut(np_offset);
let src = &src_part[current_power_offset..current_power_offset + pn];
limbs_square_to_out(&mut dst_part[..pn << 1], src, &mut scratch);
}
pn = nn;
if powers_storage[np_offset + nn] != 0 {
pn += 1;
}
current_power_is_vp = false;
current_power_offset = np_offset;
}
let mut pwr = usize::low_mask(u64::exact_from(npowers));
for i in (0..npowers).rev() {
let pn = pwpsn[i];
if qn < pn || pwr + usize::power_of_2(u64::exact_from(i)) > cap {
continue;
}
let power_slice = if pwpsp_offsets[i] == usize::MAX {
&vp[..pn] } else {
let offset = pwpsp_offsets[i];
&powers_storage[offset..offset + pn]
};
qp[qn] = 0;
limbs_modular_div_mod_wrap(
&mut qp2[..=(qn - pn)],
&mut tp[..pn],
&qp[..=qn],
power_slice,
);
if !slice_test_zero(&tp[..pn]) && limbs_cmp_same_length(&tp[..pn], power_slice) != Equal {
continue;
}
swap(&mut qp, &mut qp2);
qn -= pn;
if qp[qn] != 0 {
qn += 1;
}
pwr += usize::power_of_2(u64::exact_from(i));
}
wp.clear();
wp.extend_from_slice(&qp[..qn]);
pwr
}
fn remove_odd_power(x: &Natural, y: &Natural, cap: usize) -> (Natural, u64) {
let mut out = Vec::new();
let k = limbs_remove(&mut out, &x.to_limbs_asc(), &y.to_limbs_asc(), cap);
(Natural::from_owned_limbs_asc(out), u64::exact_from(k))
}
fn remove_power_helper(x: &Natural, y: &Natural) -> (Natural, u64) {
assert!(*y > 1u32, "Cannot remove powers of {y}");
if *x == 0u32 {
return (Natural::ZERO, 0);
}
if x < y {
return (x.clone(), 0);
}
if let (Natural(Small(sx)), Natural(Small(sy))) = (x, y) {
let (q, k) = sx.remove_power(*sy);
return (Natural::from(q), k);
}
let cap = usize::exact_from(x.significant_bits());
let two_pow = y.trailing_zeros().unwrap();
if two_pow == 0 {
return remove_odd_power(x, y, cap);
}
let two_limit = x.trailing_zeros().unwrap() / two_pow;
let odd = y >> two_pow;
if odd == 1u32 {
return (x >> (two_pow * two_limit), two_limit);
}
let (q, odd_limit) = remove_odd_power(x, &odd, cap);
let k = min(two_limit, odd_limit);
let q = if k == odd_limit {
q
} else {
q * (&odd).pow(odd_limit - k)
};
(q >> (two_pow * k), k)
}
impl RemovePower<Self> for Natural {
type Output = Self;
#[inline]
fn remove_power(self, other: Self) -> (Self, u64) {
remove_power_helper(&self, &other)
}
}
impl RemovePower<&Self> for Natural {
type Output = Self;
#[inline]
fn remove_power(self, other: &Self) -> (Self, u64) {
remove_power_helper(&self, other)
}
}
impl RemovePower<Natural> for &Natural {
type Output = Natural;
#[inline]
fn remove_power(self, other: Natural) -> (Natural, u64) {
remove_power_helper(self, &other)
}
}
impl RemovePower<&Natural> for &Natural {
type Output = Natural;
#[inline]
fn remove_power(self, other: &Natural) -> (Natural, u64) {
remove_power_helper(self, other)
}
}
impl RemovePowerAssign<Self> for Natural {
#[inline]
fn remove_power_assign(&mut self, other: Self) -> u64 {
let (q, k) = remove_power_helper(self, &other);
*self = q;
k
}
}
impl RemovePowerAssign<&Self> for Natural {
#[inline]
fn remove_power_assign(&mut self, other: &Self) -> u64 {
let (q, k) = remove_power_helper(self, other);
*self = q;
k
}
}