use crate::natural::arithmetic::add::{
limbs_add_same_length_to_out, 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::limb::limbs_mul_limb_to_out;
use crate::natural::arithmetic::mul::toom::{TUNE_PROGRAM_BUILD, WANT_FAT_BINARY};
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::{
DoubleLimb, Limb, MUL_TOOM8H_THRESHOLD, MUL_TOOM22_THRESHOLD, MUL_TOOM33_THRESHOLD,
MUL_TOOM44_THRESHOLD, MULLO_BASECASE_THRESHOLD, MULLO_DC_THRESHOLD, MULLO_MUL_N_THRESHOLD,
};
use malachite_base::num::arithmetic::traits::WrappingAddAssign;
pub_crate_test! {limbs_mul_low_same_length_basecase(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) {
let n = xs.len();
assert_ne!(n, 0);
assert_eq!(ys.len(), n);
let (ys_last, ys_init) = ys.split_last().unwrap();
let (out_last, out_init) = out[..n].split_last_mut().unwrap();
let mut p = xs[0].wrapping_mul(*ys_last);
if n != 1 {
let y = ys_init[0];
let (xs_last, xs_init) = xs.split_last().unwrap();
let product = xs_last
.wrapping_mul(y)
.wrapping_add(limbs_mul_limb_to_out::<DoubleLimb, Limb>(out_init, xs_init, y));
p.wrapping_add_assign(product);
let m = n - 1;
for i in 1..m {
let y = ys_init[i];
let (xs_lo, xs_hi) = xs_init.split_at(m - i);
let limb_p = xs_hi[0].wrapping_mul(y).wrapping_add(
limbs_slice_add_mul_limb_same_length_in_place_left(&mut out_init[i..], xs_lo, y),
);
p.wrapping_add_assign(limb_p);
}
}
*out_last = p;
}}
const SCALED_MUL_TOOM22_THRESHOLD: usize = MUL_TOOM22_THRESHOLD * 36 / (36 - 11);
const SCALED_MUL_TOOM33_THRESHOLD: usize = MUL_TOOM33_THRESHOLD * 36 / (36 - 11);
const SCALED_MUL_TOOM44_THRESHOLD: usize = MUL_TOOM44_THRESHOLD.saturating_mul(40) / (40 - 9);
const SCALED_MUL_TOOM8H_THRESHOLD: usize = MUL_TOOM8H_THRESHOLD * 10 / 9;
const MAYBE_RANGE_BASECASE_MUL_LOW: bool = TUNE_PROGRAM_BUILD
|| WANT_FAT_BINARY
|| (MULLO_DC_THRESHOLD == 0 && MULLO_BASECASE_THRESHOLD < SCALED_MUL_TOOM22_THRESHOLD
|| MULLO_DC_THRESHOLD != 0 && MULLO_DC_THRESHOLD < SCALED_MUL_TOOM22_THRESHOLD);
const MAYBE_RANGE_TOOM22_MUL_LOW: bool = TUNE_PROGRAM_BUILD
|| WANT_FAT_BINARY
|| (MULLO_DC_THRESHOLD == 0 && MULLO_BASECASE_THRESHOLD < SCALED_MUL_TOOM33_THRESHOLD
|| MULLO_DC_THRESHOLD != 0 && MULLO_DC_THRESHOLD < SCALED_MUL_TOOM33_THRESHOLD);
const fn get_n_lo(n: usize) -> usize {
if MAYBE_RANGE_BASECASE_MUL_LOW && n < SCALED_MUL_TOOM22_THRESHOLD {
n >> 1
} else if MAYBE_RANGE_TOOM22_MUL_LOW && n < SCALED_MUL_TOOM33_THRESHOLD {
n * 11 / 36 } else if n < SCALED_MUL_TOOM44_THRESHOLD {
n * 9 / 40 } else if n < SCALED_MUL_TOOM8H_THRESHOLD {
n * 7 / 39 } else {
n / 10 }
}
pub_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_mul_low_same_length_divide_and_conquer_shared_scratch(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb],
) {
let n = xs.len();
assert_eq!(ys.len(), n);
assert!(n >= 2);
let n_lo = get_n_lo(n);
let n_hi = n - n_lo;
let (xs_lo, xs_hi) = xs.split_at(n_hi);
let mut mul_scratch = vec![0; limbs_mul_same_length_to_out_scratch_len(n_hi)];
limbs_mul_same_length_to_out(out, xs_lo, &ys[..n_hi], &mut mul_scratch);
let ys_lo = &ys[..n_lo];
let (out_lo, out_hi) = out.split_at_mut(n);
if n_lo < MULLO_BASECASE_THRESHOLD {
limbs_mul_greater_to_out_basecase(out_hi, xs_hi, ys_lo);
} else if n_lo < MULLO_DC_THRESHOLD {
limbs_mul_low_same_length_basecase(out_hi, xs_hi, ys_lo);
} else {
limbs_mul_low_same_length_divide_and_conquer_shared_scratch(out_hi, xs_hi, ys_lo);
}
limbs_slice_add_same_length_in_place_left(&mut out_lo[n_hi..], &out_hi[..n_lo]);
let xs_lo = &xs[..n_lo];
let ys_hi = &ys[n_hi..];
if n_lo < MULLO_BASECASE_THRESHOLD {
limbs_mul_greater_to_out_basecase(out_hi, xs_lo, ys_hi);
} else if n_lo < MULLO_DC_THRESHOLD {
limbs_mul_low_same_length_basecase(out_hi, xs_lo, ys_hi);
} else {
limbs_mul_low_same_length_divide_and_conquer_shared_scratch(out_hi, xs_lo, ys_hi);
}
limbs_slice_add_same_length_in_place_left(&mut out_lo[n_hi..], &out_hi[..n_lo]);
}}
pub_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_mul_low_same_length_divide_and_conquer(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb],
scratch: &mut [Limb],
) {
let n = xs.len();
assert_eq!(ys.len(), n);
assert!(n >= 2);
let n_lo = get_n_lo(n);
let n_hi = n - n_lo;
let (out_lo, out_hi) = out[..n].split_at_mut(n_hi);
let (xs_lo, xs_hi) = xs.split_at(n_hi);
let mut mul_scratch = vec![0; limbs_mul_same_length_to_out_scratch_len(n_hi)];
limbs_mul_same_length_to_out(scratch, xs_lo, &ys[..n_hi], &mut mul_scratch);
out_lo.copy_from_slice(&scratch[..n_hi]);
let ys_lo = &ys[..n_lo];
let (scratch_lo, scratch_hi) = scratch.split_at_mut(n);
if n_lo < MULLO_BASECASE_THRESHOLD {
limbs_mul_greater_to_out_basecase(scratch_hi, xs_hi, ys_lo);
} else if n_lo < MULLO_DC_THRESHOLD {
limbs_mul_low_same_length_basecase(scratch_hi, xs_hi, ys_lo);
} else {
limbs_mul_low_same_length_divide_and_conquer_shared_scratch(scratch_hi, xs_hi, ys_lo);
}
limbs_add_same_length_to_out(out_hi, &scratch_lo[n_hi..], &scratch_hi[..n_lo]);
let xs_lo = &xs[..n_lo];
let ys_hi = &ys[n_hi..];
if n_lo < MULLO_BASECASE_THRESHOLD {
limbs_mul_greater_to_out_basecase(scratch_hi, xs_lo, ys_hi);
} else if n_lo < MULLO_DC_THRESHOLD {
limbs_mul_low_same_length_basecase(scratch_hi, xs_lo, ys_hi);
} else {
limbs_mul_low_same_length_divide_and_conquer_shared_scratch(scratch_hi, xs_lo, ys_hi);
}
limbs_slice_add_same_length_in_place_left(out_hi, &scratch_hi[..n_lo]);
}}
pub_const_test! {limbs_mul_low_same_length_divide_and_conquer_scratch_len(n: usize) -> usize {
n << 1
}}
const MULLO_BASECASE_THRESHOLD_LIMIT: usize = MULLO_BASECASE_THRESHOLD;
pub_test! {limbs_mul_low_same_length_large(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb],
scratch: &mut [Limb],
) {
let n = xs.len();
let mut mul_scratch = vec![0; limbs_mul_same_length_to_out_scratch_len(n)];
limbs_mul_same_length_to_out(scratch, xs, ys, &mut mul_scratch);
out.copy_from_slice(&scratch[..n]);
}}
pub_crate_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_mul_low_same_length(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) {
let n = xs.len();
assert_eq!(ys.len(), n);
assert!(n >= 1);
let out = &mut out[..n];
if n < MULLO_BASECASE_THRESHOLD {
let scratch = &mut [0; MULLO_BASECASE_THRESHOLD_LIMIT];
limbs_mul_greater_to_out_basecase(scratch, xs, ys);
out.copy_from_slice(&scratch[..n]);
} else if n < MULLO_DC_THRESHOLD {
limbs_mul_low_same_length_basecase(out, xs, ys);
} else {
let mut scratch = vec![0; limbs_mul_low_same_length_divide_and_conquer_scratch_len(n)];
if n < MULLO_MUL_N_THRESHOLD {
limbs_mul_low_same_length_divide_and_conquer(out, xs, ys, &mut scratch);
} else {
limbs_mul_low_same_length_large(out, xs, ys, &mut scratch);
}
}
}}
pub_crate_test! {limbs_mul_low_same_length_basecase_alt(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb]
) {
let n = xs.len();
assert_ne!(n, 0);
assert_eq!(ys.len(), n);
let out = &mut out[..n];
limbs_mul_limb_to_out::<DoubleLimb, Limb>(out, xs, ys[0]);
for i in 1..n {
limbs_slice_add_mul_limb_same_length_in_place_left(&mut out[i..], &xs[..n - i], ys[i]);
}
}}