use crate::natural::arithmetic::add::{
limbs_slice_add_limb_in_place, limbs_slice_add_same_length_in_place_left,
};
use crate::natural::arithmetic::add_mul::limbs_slice_add_mul_limb_same_length_in_place_left;
use crate::natural::arithmetic::mul::{
limbs_mul_greater_to_out_basecase, limbs_mul_same_length_to_out,
limbs_mul_same_length_to_out_scratch_len,
};
use crate::platform::{Limb, MUL_FFT_THRESHOLD};
use core::cmp::max;
use malachite_base::num::arithmetic::traits::XMulYToZZ;
use malachite_base::num::conversion::traits::WrappingFrom;
pub(crate) const MPFR_MULHIGH_TAB: [i8; 17] =
[-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
fn limbs_mul_high_same_length_basecase(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) {
let len = xs.len();
assert_eq!(ys.len(), len);
let out = &mut out[len - 1..];
(out[1], out[0]) = Limb::x_mul_y_to_zz(*xs.last().unwrap(), ys[0]);
for (i, y) in ys.iter().enumerate().skip(1) {
let (out_lo, out_hi) = out.split_at_mut(i + 1);
out_hi[0] =
limbs_slice_add_mul_limb_same_length_in_place_left(out_lo, &xs[len - i - 1..], *y);
}
}
pub(crate) fn limbs_mul_high_same_length_scratch_len(len: usize) -> usize {
if len > MUL_FFT_THRESHOLD {
limbs_mul_same_length_to_out_scratch_len(len)
} else {
let k = MPFR_MULHIGH_TAB.get(len).map_or_else(
|| 3 * (len >> 2),
|&m| if m == -1 { 0 } else { usize::wrapping_from(m) },
);
if k == 0 {
0
} else {
let l = len - k;
max(
limbs_mul_same_length_to_out_scratch_len(k),
limbs_mul_high_same_length_scratch_len(l),
)
}
}
}
pub(crate) fn limbs_mul_high_same_length(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb],
scratch: &mut [Limb],
) {
let len = xs.len();
assert_eq!(ys.len(), len);
const LENGTH_VALID: bool = MPFR_MULHIGH_TAB.len() >= 8;
assert!(LENGTH_VALID); let k = MPFR_MULHIGH_TAB.get(len).map_or_else(
|| Some(3 * (len >> 2)),
|&m| {
if m == -1 {
None
} else {
Some(usize::wrapping_from(m))
}
},
);
assert!(k.is_none() || k == Some(0) || (k.unwrap() >= (len + 4) >> 1 && k.unwrap() < len));
if let Some(k) = k {
if k == 0 {
limbs_mul_high_same_length_basecase(out, xs, ys);
} else if len > MUL_FFT_THRESHOLD {
limbs_mul_same_length_to_out(out, xs, ys, scratch);
} else {
let l = len - k;
let out = &mut out[..len << 1];
let (out_lo, out_hi) = out.split_at_mut(l << 1);
let (ys_lo, ys_hi) = ys.split_at(l);
limbs_mul_same_length_to_out(out_hi, &xs[l..], ys_hi, scratch);
limbs_mul_high_same_length(out_lo, &xs[k..], ys_lo, scratch);
let out_hi = &mut out_hi[k - l - 1..k];
let mut carry = Limb::from(limbs_slice_add_same_length_in_place_left(
out_hi,
&out_lo[l - 1..],
));
limbs_mul_high_same_length(out_lo, &xs[..l], &ys[k..], scratch);
if limbs_slice_add_same_length_in_place_left(out_hi, &out_lo[l - 1..]) {
carry += 1;
}
limbs_slice_add_limb_in_place(&mut out[len + l..], carry);
}
} else {
limbs_mul_greater_to_out_basecase(out, xs, ys);
}
}