use crate::integer_polynomial::IntegerPolynomial;
use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
use crate::integer_polynomial::arithmetic::pow::addchains::{
addition_chain, addition_chain_is_shorter, pow_to_out_addchains,
};
use crate::integer_polynomial::arithmetic::pow::binexp::pow_to_out_binexp;
use crate::integer_polynomial::arithmetic::pow::binomial::pow_to_out_binomial;
use crate::integer_polynomial::arithmetic::pow::multinomial::pow_to_out_multinomial;
use crate::integer_polynomial::arithmetic::pow::small::pow_to_out_small;
use crate::integer_polynomial::arithmetic::square::square_to_out;
use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
use alloc::vec;
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::{Pow, PowAssign};
use malachite_base::num::conversion::traits::ExactFrom;
pub mod addchains;
pub mod binexp;
pub mod binomial;
pub mod multinomial;
pub mod small;
pub(crate) fn multinomial_preferred(len: u64, bits: u64, e: u64) -> bool {
len <= 16
&& len * bits <= 3072
&& e >= (len << 2) * (bits >> 8).max(1)
&& e.saturating_mul(bits) >= (len << 6).max(512)
}
pub(crate) fn multinomial_preferred_for_binomial(bits: u64, e: u64) -> bool {
e >= 64 && (32..=1024).contains(&bits)
}
pub(crate) fn addition_chain_preferred(len: u64, bits: u64, e: u64) -> bool {
e <= 148
&& ((e <= 7 && bits >= 32) || addition_chain_is_shorter(e))
&& (len >= 8 || (len >= 4 && (bits >= 1024 || e <= 7)) || bits >= 4096)
}
crate_test_fn! {pow_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], e: u64) {
let len = u64::exact_from(xs.len());
if e < 5 {
pow_to_out_small(out, xs, e);
return;
}
let bits = vec_max_bits(xs).0;
if len == 2 {
if multinomial_preferred_for_binomial(bits, e) {
pow_to_out_multinomial(out, xs, e);
} else {
pow_to_out_binomial(out, xs, e);
}
} else if multinomial_preferred(len, bits, e) {
pow_to_out_multinomial(out, xs, e);
} else if addition_chain_preferred(len, bits, e) {
pow_to_out_addchains_e(out, xs, e);
} else {
pow_to_out_binexp(out, xs, e);
}
}}
crate_test_fn! {pow_to_out_addchains_e<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], e: u64) {
let (a, start) = addition_chain(e);
pow_to_out_addchains(out, xs, &a[start..]);
}}
fn power_len(len: usize, low: usize, e: u64) -> usize {
let e = usize::exact_from(e);
e.checked_mul(len - 1 + low)
.and_then(|n| n.checked_add(1))
.expect("the power has too many coefficients to represent")
}
crate_test_fn! {pow_ref_with_kernel<C: PolynomialCoefficient>(
xs: &[C],
e: u64,
pow_to_out_kernel: fn(&mut [C], &[C], u64),
) -> Vec<C> {
if e == 0 {
return vec![C::ONE];
}
let Some(low) = xs.iter().position(|x| !x.is_zero()) else {
return Vec::new();
};
let q = &xs[low..];
let mut out = vec![C::ZERO; power_len(q.len(), low, e)];
let out_q = &mut out[usize::exact_from(e) * low..];
match (q.len(), e) {
(1, _) => out_q[0] = q[0].pow_ref(e),
(_, 1) => out_q.clone_from_slice(q),
(_, 2) => square_to_out(out_q, q),
_ => pow_to_out_kernel(out_q, q, e),
}
out
}}
#[inline]
pub(crate) fn pow_ref<C: PolynomialCoefficient>(xs: &[C], e: u64) -> Vec<C> {
pow_ref_with_kernel(xs, e, pow_to_out)
}
pub(crate) fn pow_assign_vec<C: PolynomialCoefficient + PowAssign<u64>>(xs: &mut Vec<C>, e: u64) {
match (xs.len(), e) {
(0, 0) => xs.push(C::ONE),
(_, 0) => {
xs.truncate(1);
xs[0] = C::ONE;
}
(0, _) | (_, 1) => {}
(1, _) => xs[0].pow_assign(e),
_ => *xs = pow_ref(xs, e),
}
}
impl Pow<u64> for IntegerPolynomial {
type Output = Self;
#[inline]
fn pow(mut self, exp: u64) -> Self {
self.pow_assign(exp);
self
}
}
impl Pow<u64> for &IntegerPolynomial {
type Output = IntegerPolynomial;
#[inline]
fn pow(self, exp: u64) -> IntegerPolynomial {
IntegerPolynomial {
coefficients: pow_ref(&self.coefficients, exp),
}
}
}
impl PowAssign<u64> for IntegerPolynomial {
#[inline]
fn pow_assign(&mut self, exp: u64) {
pow_assign_vec(&mut self.coefficients, exp);
}
}