use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
use crate::integer_polynomial::arithmetic::mul_middle::classical::mul_middle_to_out_classical;
use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
use crate::integer_polynomial::arithmetic::mul_middle::kronecker::mul_middle_to_out_kronecker;
use crate::integer_polynomial::arithmetic::mul_middle::schonhage_strassen::*;
use crate::integer_polynomial::arithmetic::mul_middle::tiny::{
mul_middle_to_out_tiny_1, mul_middle_to_out_tiny_2,
};
use crate::integer_polynomial::arithmetic::mul_truncated::karatsuba::mul_truncated_to_out_karatsuba;
use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
use crate::integer_polynomial::arithmetic::vec::{
TinyKernel, classical_preferred, fft_preferred, karatsuba_preferred,
schonhage_strassen_preferred, tiny_kernel,
};
use core::cmp::min;
use core::mem::swap;
use core::ptr;
use malachite_base::num::conversion::traits::ExactFrom;
pub mod classical;
pub mod fft;
pub mod kronecker;
pub mod schonhage_strassen;
pub mod tiny;
pub(crate) fn truncate_mul_middle_inputs<'a, C: PolynomialCoefficient>(
xs: &'a [C],
ys: &'a [C],
mut nlo: usize,
mut nhi: usize,
) -> (&'a [C], &'a [C], usize, usize) {
let mut xs = &xs[..min(xs.len(), nhi)];
let mut ys = &ys[..min(ys.len(), nhi)];
let nlo2 = xs.len() + ys.len() - 1 - nlo;
if xs.len() > nlo2 {
let trunc = xs.len() - nlo2;
xs = &xs[trunc..];
nlo -= trunc;
nhi -= trunc;
}
if ys.len() > nlo2 {
let trunc = ys.len() - nlo2;
ys = &ys[trunc..];
nlo -= trunc;
nhi -= trunc;
}
(xs, ys, nlo, nhi)
}
crate_test_fn! {mul_middle_to_out<C: PolynomialCoefficient>(
out: &mut [C],
xs: &[C],
ys: &[C],
nlo: usize,
nhi: usize,
) {
assert_ne!(xs.len(), 0);
assert_ne!(ys.len(), 0);
assert!(nlo < nhi);
assert!(nhi < xs.len() + ys.len());
let (mut xs, mut ys, nlo, nhi) = truncate_mul_middle_inputs(xs, ys, nlo, nhi);
let len = nhi - nlo;
if xs.len() < ys.len() {
swap(&mut xs, &mut ys);
}
if ys.len() == 1 {
C::vec_mul_scalar_to_out(out, &xs[nlo..nhi], &ys[0]);
return;
}
let bits1 = vec_max_bits(xs).0;
let bits2 = if ptr::eq(xs, ys) { bits1 } else { vec_max_bits(ys).0 };
let len1 = u64::exact_from(xs.len());
let len2 = u64::exact_from(ys.len());
if fft_preferred(len2, bits1, bits2, 100, 200) && mul_middle_to_out_fft(out, xs, ys, nlo, nhi) {
return;
}
let len = u64::exact_from(len);
let short_enough = len2 < 50 || (len2 << 2 >= 3 * len && len < 150 + bits1 + bits2);
match tiny_kernel(bits1, bits2, len2, short_enough) {
Some(TinyKernel::OneWord) => mul_middle_to_out_tiny_1(out, xs, ys, nlo, nhi),
Some(TinyKernel::TwoWord) => mul_middle_to_out_tiny_2(out, xs, ys, nlo, nhi),
None if nhi <= 8 || classical_preferred(len2, bits1, bits2) || len <= 3 => {
mul_middle_to_out_classical(out, xs, ys, nlo, nhi);
}
None if nlo == 0 && karatsuba_preferred(len2, bits1, bits2) => {
mul_truncated_to_out_karatsuba(out, xs, ys);
}
None if schonhage_strassen_preferred(len1, len2, bits1, bits2, 3071) => {
mul_middle_to_out_schonhage_strassen(out, xs, ys, nlo, nhi);
}
None => mul_middle_to_out_kronecker(out, xs, ys, nlo, nhi),
}
}}