use crate::integer_polynomial::arithmetic::mul::{mul_ref_ref, mul_val_ref, mul_val_val};
use crate::natural::Natural;
use crate::natural::arithmetic::add::limbs_slice_add_same_length_in_place_left;
use crate::natural::arithmetic::mul::mul_low::limbs_mul_low_same_length;
use crate::natural::arithmetic::sub::limbs_sub_same_length_in_place_left;
use crate::natural_polynomial::NaturalPolynomial;
use crate::natural_polynomial::arithmetic::mod_power_of_2_add::assert_reduced;
use crate::platform::Limb;
use alloc::vec;
use alloc::vec::Vec;
use core::cmp::{max, min};
use core::mem::take;
use malachite_base::num::arithmetic::traits::{
ModPowerOf2Assign, ModPowerOf2Mul, ModPowerOf2MulAssign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::slices::slice_test_zero;
use malachite_base::unsigned_polynomial::arithmetic::mod_power_of_2_mul::mod_power_of_2_mul_to_out;
pub(crate) fn mul_slots_karatsuba_threshold(slot_len: usize) -> usize {
max(4, 96 / slot_len)
}
pub(crate) fn square_slots_karatsuba_threshold(slot_len: usize) -> usize {
max(4, 2048 / (slot_len * slot_len))
}
pub(crate) fn slot_len(pow: u64) -> usize {
usize::exact_from(pow.div_ceil(Limb::WIDTH))
}
pub(crate) fn naturals_to_slots(xs: &[Natural], slot_len: usize) -> Vec<Limb> {
let mut slots = vec![0; xs.len() * slot_len];
for (slot, x) in slots.chunks_exact_mut(slot_len).zip(xs) {
let limbs = x.as_limbs_asc();
slot[..limbs.len()].copy_from_slice(limbs);
}
slots
}
pub(crate) fn slots_to_naturals(slots: &[Limb], slot_len: usize, pow: u64) -> Vec<Natural> {
let mut slots = slots.to_vec();
let rem = pow & Limb::WIDTH_MASK;
slots
.chunks_exact_mut(slot_len)
.map(|slot| {
if rem != 0 {
slot[slot_len - 1].mod_power_of_2_assign(rem);
}
Natural::from_limbs_asc(slot)
})
.collect()
}
pub(crate) fn slots_add_assign(xs: &mut [Limb], ys: &[Limb], slot_len: usize) {
for (x, y) in xs.chunks_exact_mut(slot_len).zip(ys.chunks_exact(slot_len)) {
limbs_slice_add_same_length_in_place_left(x, y);
}
}
pub(crate) fn slots_sub_assign(xs: &mut [Limb], ys: &[Limb], slot_len: usize) {
for (x, y) in xs.chunks_exact_mut(slot_len).zip(ys.chunks_exact(slot_len)) {
limbs_sub_same_length_in_place_left(x, y);
}
}
pub(crate) fn mul_slots_classical(out: &mut [Limb], xs: &[Limb], ys: &[Limb], slot_len: usize) {
out.fill(0);
let mut product = vec![0; slot_len];
for (i, x) in xs.chunks_exact(slot_len).enumerate() {
if slice_test_zero(x) {
continue;
}
for (o, y) in out[i * slot_len..]
.chunks_exact_mut(slot_len)
.zip(ys.chunks_exact(slot_len))
{
limbs_mul_low_same_length(&mut product, x, y);
limbs_slice_add_same_length_in_place_left(o, &product);
}
}
}
pub(crate) fn mul_slots_karatsuba(out: &mut [Limb], xs: &[Limb], ys: &[Limb], slot_len: usize) {
let (xs, ys) = if xs.len() >= ys.len() {
(xs, ys)
} else {
(ys, xs)
};
let n = xs.len() / slot_len;
let m = ys.len() / slot_len;
if m < mul_slots_karatsuba_threshold(slot_len) {
mul_slots_classical(out, xs, ys, slot_len);
return;
}
if n > m {
out.fill(0);
let mut product = vec![0; ((m << 1) - 1) * slot_len];
for (k, piece) in xs.chunks(m * slot_len).enumerate() {
let product = &mut product[..piece.len() + (m - 1) * slot_len];
mul_slots_karatsuba(product, piece, ys, slot_len);
slots_add_assign(&mut out[k * m * slot_len..], product, slot_len);
}
return;
}
let h = n >> 1;
let split = h * slot_len;
let low_len = ((h << 1) - 1) * slot_len;
let (x0, x1) = xs.split_at(split);
let (y0, y1) = ys.split_at(split);
let (low, high) = out.split_at_mut(split << 1);
mul_slots_karatsuba(&mut low[..low_len], x0, y0, slot_len);
low[low_len..].fill(0);
mul_slots_karatsuba(high, x1, y1, slot_len);
let mut x_sum = x1.to_vec();
slots_add_assign(&mut x_sum, x0, slot_len);
let mut y_sum = y1.to_vec();
slots_add_assign(&mut y_sum, y0, slot_len);
let mut middle = vec![0; (((n - h) << 1) - 1) * slot_len];
mul_slots_karatsuba(&mut middle, &x_sum, &y_sum, slot_len);
slots_sub_assign(&mut middle, &out[..low_len], slot_len);
slots_sub_assign(&mut middle, &out[split << 1..], slot_len);
slots_add_assign(&mut out[split..], &middle, slot_len);
}
crate_test_fn! {mod_power_of_2_mul_low_classical(
xs: &[Natural],
ys: &[Natural],
pow: u64,
) -> Vec<Natural> {
let len = xs.len() + ys.len() - 1;
if pow == 0 {
return vec![Natural::ZERO; len];
}
let slot_len = slot_len(pow);
let mut out = vec![0; len * slot_len];
mul_slots_classical(
&mut out,
&naturals_to_slots(xs, slot_len),
&naturals_to_slots(ys, slot_len),
slot_len,
);
slots_to_naturals(&out, slot_len, pow)
}}
crate_test_fn! {mod_power_of_2_mul_low_karatsuba(
xs: &[Natural],
ys: &[Natural],
pow: u64,
) -> Vec<Natural> {
let len = xs.len() + ys.len() - 1;
if pow == 0 {
return vec![Natural::ZERO; len];
}
if pow <= Limb::WIDTH {
let xs: Vec<Limb> = xs.iter().map(Limb::exact_from).collect();
let ys: Vec<Limb> = ys.iter().map(Limb::exact_from).collect();
let mut out = vec![0; len];
mod_power_of_2_mul_to_out(&mut out, &xs, &ys, pow);
return out.into_iter().map(Natural::from).collect();
}
let slot_len = slot_len(pow);
let mut out = vec![0; len * slot_len];
mul_slots_karatsuba(
&mut out,
&naturals_to_slots(xs, slot_len),
&naturals_to_slots(ys, slot_len),
slot_len,
);
slots_to_naturals(&out, slot_len, pow)
}}
pub(crate) fn reduce_coefficients(mut xs: Vec<Natural>, pow: u64) -> NaturalPolynomial {
for x in &mut xs {
x.mod_power_of_2_assign(pow);
}
let mut p = NaturalPolynomial { coefficients: xs };
p.trim();
p
}
pub(crate) const MUL_LOW_WINDOWS: [(u64, usize); 11] = [
(16, 150),
(32, 500),
(48, 1000),
(64, 2000),
(96, 24),
(128, 64),
(300, 100),
(500, 48),
(2000, 32),
(5000, 8),
(u64::MAX, 0),
];
pub(crate) fn low_preferred(windows: &[(u64, usize)], len2: usize, pow: u64) -> bool {
windows
.iter()
.find(|&&(max_pow, _)| pow <= max_pow)
.is_some_and(|&(_, max_len)| len2 <= max_len)
}
fn mul_low_preferred(len1: usize, len2: usize, pow: u64) -> bool {
len1 > 1 && len2 > 1 && low_preferred(&MUL_LOW_WINDOWS, min(len1, len2), pow)
}
pub(crate) fn mod_power_of_2_mul_val_val(
xs: Vec<Natural>,
ys: Vec<Natural>,
pow: u64,
) -> NaturalPolynomial {
if mul_low_preferred(xs.len(), ys.len(), pow) {
reduce_coefficients(mod_power_of_2_mul_low_karatsuba(&xs, &ys, pow), pow)
} else {
reduce_coefficients(mul_val_val(xs, ys), pow)
}
}
pub(crate) fn mod_power_of_2_mul_val_ref(
xs: Vec<Natural>,
ys: &[Natural],
pow: u64,
) -> NaturalPolynomial {
if mul_low_preferred(xs.len(), ys.len(), pow) {
reduce_coefficients(mod_power_of_2_mul_low_karatsuba(&xs, ys, pow), pow)
} else {
reduce_coefficients(mul_val_ref(xs, ys), pow)
}
}
pub(crate) fn mod_power_of_2_mul_ref_ref(
xs: &[Natural],
ys: &[Natural],
pow: u64,
) -> NaturalPolynomial {
if mul_low_preferred(xs.len(), ys.len(), pow) {
reduce_coefficients(mod_power_of_2_mul_low_karatsuba(xs, ys, pow), pow)
} else {
reduce_coefficients(mul_ref_ref(xs, ys), pow)
}
}
crate_test_fn! {mod_power_of_2_mul_full(xs: &[Natural], ys: &[Natural], pow: u64) -> Vec<Natural> {
let mut out = mul_ref_ref(xs, ys);
for x in &mut out {
x.mod_power_of_2_assign(pow);
}
out
}}
impl ModPowerOf2Mul<Self> for NaturalPolynomial {
type Output = Self;
fn mod_power_of_2_mul(self, other: Self, pow: u64) -> Self {
assert_reduced(&self, &other, pow);
mod_power_of_2_mul_val_val(self.coefficients, other.coefficients, pow)
}
}
impl ModPowerOf2Mul<&Self> for NaturalPolynomial {
type Output = Self;
fn mod_power_of_2_mul(self, other: &Self, pow: u64) -> Self {
assert_reduced(&self, other, pow);
mod_power_of_2_mul_val_ref(self.coefficients, &other.coefficients, pow)
}
}
impl ModPowerOf2Mul<NaturalPolynomial> for &NaturalPolynomial {
type Output = NaturalPolynomial;
fn mod_power_of_2_mul(self, other: NaturalPolynomial, pow: u64) -> NaturalPolynomial {
assert_reduced(self, &other, pow);
mod_power_of_2_mul_val_ref(other.coefficients, &self.coefficients, pow)
}
}
impl ModPowerOf2Mul<&NaturalPolynomial> for &NaturalPolynomial {
type Output = NaturalPolynomial;
fn mod_power_of_2_mul(self, other: &NaturalPolynomial, pow: u64) -> NaturalPolynomial {
assert_reduced(self, other, pow);
mod_power_of_2_mul_ref_ref(&self.coefficients, &other.coefficients, pow)
}
}
impl ModPowerOf2MulAssign<Self> for NaturalPolynomial {
fn mod_power_of_2_mul_assign(&mut self, other: Self, pow: u64) {
assert_reduced(self, &other, pow);
*self = mod_power_of_2_mul_val_val(take(&mut self.coefficients), other.coefficients, pow);
}
}
impl ModPowerOf2MulAssign<&Self> for NaturalPolynomial {
fn mod_power_of_2_mul_assign(&mut self, other: &Self, pow: u64) {
assert_reduced(self, other, pow);
*self = mod_power_of_2_mul_val_ref(take(&mut self.coefficients), &other.coefficients, pow);
}
}