use crate::integer_polynomial::IntegerPolynomial;
use crate::integer_polynomial::arithmetic::coefficient::{
PolynomialCoefficient, trim_coefficients, truncate_coefficients,
};
use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
use crate::integer_polynomial::arithmetic::mul_truncated::classical::mul_truncated_to_out_classical;
use crate::integer_polynomial::arithmetic::mul_truncated::karatsuba::mul_truncated_to_out_karatsuba;
use crate::integer_polynomial::arithmetic::mul_truncated::kronecker::mul_truncated_to_out_kronecker;
use crate::integer_polynomial::arithmetic::mul_truncated::schonhage_strassen::*;
use crate::integer_polynomial::arithmetic::mul_truncated::tiny::{
mul_truncated_to_out_tiny_1, mul_truncated_to_out_tiny_2,
};
use crate::integer_polynomial::arithmetic::square_truncated::square_truncated_to_out;
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 alloc::vec;
use alloc::vec::Vec;
use core::cmp::min;
use core::mem::{swap, take};
use core::ptr;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::polynomial::{MulTruncated, MulTruncatedAssign};
pub mod classical;
pub mod karatsuba;
pub mod kronecker;
pub mod schonhage_strassen;
pub mod tiny;
crate_test_fn! {mul_truncated_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], ys: &[C]) {
let n = out.len();
let mut xs = &xs[..min(xs.len(), n)];
let mut ys = &ys[..min(ys.len(), n)];
assert_ne!(n, 0);
assert_ne!(xs.len(), 0);
assert_ne!(ys.len(), 0);
assert!(n < xs.len() + ys.len());
if xs.len() < ys.len() {
swap(&mut xs, &mut ys);
}
if ys.len() == 1 {
C::vec_mul_scalar_to_out(out, xs, &ys[0]);
return;
}
if ptr::eq(xs, ys) {
square_truncated_to_out(out, xs);
return;
}
let bits1 = vec_max_bits(xs).0;
let bits2 = 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, 0, n) {
return;
}
let n = u64::exact_from(n);
let short_enough = len2 < 50 || (len2 << 2 >= 3 * n && n < 150 + bits1 + bits2);
match tiny_kernel(bits1, bits2, len2, short_enough) {
Some(TinyKernel::OneWord) => mul_truncated_to_out_tiny_1(out, xs, ys),
Some(TinyKernel::TwoWord) => mul_truncated_to_out_tiny_2(out, xs, ys),
None if classical_preferred(len2, bits1, bits2) => {
mul_truncated_to_out_classical(out, xs, ys);
}
None if karatsuba_preferred(len2, bits1, bits2) => {
mul_truncated_to_out_karatsuba(out, xs, ys);
}
None if schonhage_strassen_preferred(len1, len2, bits1, bits2, 4097) => {
mul_truncated_to_out_schonhage_strassen(out, xs, ys);
}
None => mul_truncated_to_out_kronecker(out, xs, ys),
}
}}
pub(crate) fn mul_truncated_ref_ref<C: PolynomialCoefficient>(
xs: &[C],
ys: &[C],
len: u64,
) -> Vec<C> {
if xs.is_empty() || ys.is_empty() || len == 0 {
return Vec::new();
}
let n = usize::try_from(len)
.unwrap_or(usize::MAX)
.min(xs.len() + ys.len() - 1);
let mut out = vec![C::ZERO; n];
mul_truncated_to_out(&mut out, xs, ys);
trim_coefficients(&mut out);
out
}
pub(crate) fn mul_truncated_val_ref<C: PolynomialCoefficient>(
mut xs: Vec<C>,
ys: &[C],
len: u64,
) -> Vec<C> {
if let [c] = ys {
truncate_coefficients(&mut xs, len);
C::vec_mul_scalar_assign(&mut xs, c);
xs
} else {
mul_truncated_ref_ref(&xs, ys, len)
}
}
pub(crate) fn mul_truncated_val_val<C: PolynomialCoefficient>(
xs: Vec<C>,
ys: Vec<C>,
len: u64,
) -> Vec<C> {
if xs.len() == 1 {
mul_truncated_val_ref(ys, &xs, len)
} else {
mul_truncated_val_ref(xs, &ys, len)
}
}
impl MulTruncated<Self> for IntegerPolynomial {
type Output = Self;
#[inline]
fn mul_truncated(self, other: Self, len: u64) -> Self {
Self {
coefficients: mul_truncated_val_val(self.coefficients, other.coefficients, len),
}
}
}
impl MulTruncated<&Self> for IntegerPolynomial {
type Output = Self;
#[inline]
fn mul_truncated(self, other: &Self, len: u64) -> Self {
Self {
coefficients: mul_truncated_val_ref(self.coefficients, &other.coefficients, len),
}
}
}
impl MulTruncated<IntegerPolynomial> for &IntegerPolynomial {
type Output = IntegerPolynomial;
#[inline]
fn mul_truncated(self, other: IntegerPolynomial, len: u64) -> IntegerPolynomial {
IntegerPolynomial {
coefficients: mul_truncated_val_ref(other.coefficients, &self.coefficients, len),
}
}
}
impl MulTruncated<&IntegerPolynomial> for &IntegerPolynomial {
type Output = IntegerPolynomial;
#[inline]
fn mul_truncated(self, other: &IntegerPolynomial, len: u64) -> IntegerPolynomial {
IntegerPolynomial {
coefficients: mul_truncated_ref_ref(&self.coefficients, &other.coefficients, len),
}
}
}
impl MulTruncatedAssign<Self> for IntegerPolynomial {
#[inline]
fn mul_truncated_assign(&mut self, other: Self, len: u64) {
self.coefficients =
mul_truncated_val_val(take(&mut self.coefficients), other.coefficients, len);
}
}
impl MulTruncatedAssign<&Self> for IntegerPolynomial {
#[inline]
fn mul_truncated_assign(&mut self, other: &Self, len: u64) {
self.coefficients =
mul_truncated_val_ref(take(&mut self.coefficients), &other.coefficients, len);
}
}