use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
use crate::platform::Limb;
use alloc::vec::Vec;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
crate_test_fn! {pow_to_out_multinomial<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], e: u64) {
let len = xs.len();
let max_multiplier = u64::exact_from(len - 1) * (e + 1);
if len >= 8 && vec_max_bits(xs).0 + max_multiplier.significant_bits() <= Limb::WIDTH {
pow_to_out_multinomial_multiples(out, xs, e);
} else {
pow_to_out_multinomial_split(out, xs, e);
}
}}
crate_test_fn! {pow_to_out_multinomial_multiples<C: PolynomialCoefficient>(
out: &mut [C],
xs: &[C],
e: u64,
) {
let len = xs.len();
let e_plus_1 = e + 1;
let multiples: Vec<C> = xs[1..]
.iter()
.zip(1..)
.map(|(x, i)| {
let mut c = C::from(i * e_plus_1);
c *= x;
c
})
.collect();
out[0] = xs[0].pow_ref(e);
let mut d = C::ZERO;
for k in 1..out.len() {
let mut weighted = C::ZERO;
let mut plain = C::ZERO;
for i in 1..=k.min(len - 1) {
weighted.add_mul_assign(&multiples[i - 1], &out[k - i]);
plain.add_mul_assign(&xs[i], &out[k - i]);
}
plain *= &C::from(u64::exact_from(k));
weighted -= &plain;
d += &xs[0];
weighted.div_exact_assign(&d);
out[k] = weighted;
}
}}
crate_test_fn! {pow_to_out_multinomial_split<C: PolynomialCoefficient>(
out: &mut [C],
xs: &[C],
e: u64,
) {
let len = xs.len();
let mut d = C::ZERO;
out[0] = xs[0].pow_ref(e);
for k in 1..out.len() {
let k_u64 = u64::exact_from(k);
let mut positive = C::ZERO;
let mut negative = C::ZERO;
let mut w = 0;
for i in 1..=k.min(len - 1) {
let t = xs[i].mul_ref(&out[k - i]);
w += e + 1;
if w >= k_u64 {
positive.add_mul_assign(&t, &C::from(w - k_u64));
} else {
negative.add_mul_assign(&t, &C::from(k_u64 - w));
}
}
positive -= &negative;
d += &xs[0];
positive.div_exact_assign(&d);
out[k] = positive;
}
}}