use crate::natural::arithmetic::div_exact::limbs_modular_div_mod_wrap;
use crate::natural::arithmetic::neg::limbs_neg_in_place;
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;
use alloc::vec::Vec;
use core::cmp::Ordering::Equal;
use core::mem::swap;
use malachite_base::num::arithmetic::traits::Parity;
use malachite_base::num::basic::integers::PrimitiveInt;
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 qp = vec![0; un + 1];
let mut qp2 = vec![0; un + 1];
let mut tp = vec![0; (un + 1 + vn) / 2];
qp[..un].copy_from_slice(up);
let mut qn = un;
let mut pwpsn = Vec::with_capacity(Limb::WIDTH as usize);
let mut pwpsp_offsets = Vec::with_capacity(Limb::WIDTH as 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;
limbs_neg_in_place(&mut qp[..=qn]);
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 = 2 * pn - 1;
if nn > qn {
break;
}
if npowers == 1 {
powers_storage = vec![0; qn + Limb::WIDTH as usize];
}
let np_offset = if npowers == 1 {
0
} else {
powers_storage.len()
};
let np_end = np_offset + 2 * pn;
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_end = current_power_offset + pn;
if src_end <= np_offset {
let (src_part, dst_part) = powers_storage.split_at_mut(np_offset);
let src = &src_part[current_power_offset..src_end];
limbs_square_to_out(&mut dst_part[..2 * pn], src, &mut scratch);
} else {
let src_data: Vec<Limb> = powers_storage[current_power_offset..src_end].to_vec();
limbs_square_to_out(
&mut powers_storage[np_offset..np_end],
&src_data,
&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 = (1usize << npowers) - 1;
for i in (0..npowers).rev() {
let pn = pwpsn[i];
if qn < pn {
continue;
}
if pwr + (1usize << 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;
limbs_neg_in_place(&mut qp[..=qn]);
if qp[qn] != 0 {
qn += 1;
}
pwr += 1usize << i;
}
wp.clear();
wp.extend_from_slice(&qp[..qn]);
pwr
}