use crate::integer_polynomial::IntegerPolynomial;
use crate::integer_polynomial::arithmetic::coefficient::{
PolynomialCoefficient, trim_coefficients, truncate_coefficients,
};
use crate::integer_polynomial::arithmetic::mul_truncated::mul_truncated_to_out;
use crate::integer_polynomial::arithmetic::pow::binexp::binexp_start;
use crate::integer_polynomial::arithmetic::square_truncated::square_truncated_to_out;
use alloc::vec;
use alloc::vec::Vec;
use core::mem::swap;
use malachite_base::num::arithmetic::traits::PowAssign;
use malachite_base::num::conversion::traits::SaturatingFrom;
use malachite_base::polynomial::{PowTruncated, PowTruncatedAssign};
crate_test_fn! {pow_truncated_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], e: u64) {
let n = out.len();
let xs = &xs[..xs.len().min(n)];
let len = xs.len();
let mut v = vec![C::ZERO; n];
let (mut bit, swaps) = binexp_start(e);
let (mut r, mut s): (&mut [C], &mut [C]) = if swaps { (&mut v, out) } else { (out, &mut v) };
let mut rlen = ((len << 1) - 1).min(n);
square_truncated_to_out(&mut r[..rlen], xs);
if bit & e != 0 {
let new_len = (rlen + len - 1).min(n);
mul_truncated_to_out(&mut s[..new_len], &r[..rlen], xs);
rlen = new_len;
swap(&mut r, &mut s);
}
loop {
bit >>= 1;
if bit == 0 {
break;
}
let new_len = ((rlen << 1) - 1).min(n);
square_truncated_to_out(&mut s[..new_len], &r[..rlen]);
rlen = new_len;
if bit & e != 0 {
let new_len = (rlen + len - 1).min(n);
mul_truncated_to_out(&mut r[..new_len], &s[..rlen], xs);
rlen = new_len;
} else {
swap(&mut r, &mut s);
}
}
}}
crate_test_fn! {pow_truncated_ref<C: PolynomialCoefficient>(xs: &[C], e: u64, len: u64) -> Vec<C> {
if len == 0 {
return Vec::new();
}
if e == 0 {
return vec![C::ONE];
}
let n = usize::saturating_from(len);
let xs = &xs[..xs.len().min(n)];
let Some(low) = xs.iter().position(|x| !x.is_zero()) else {
return Vec::new();
};
let shift = usize::saturating_from(e).saturating_mul(low);
if shift >= n {
return Vec::new();
}
let q = &xs[low..];
let q_len = (n - shift).min(
usize::saturating_from(e)
.saturating_mul(q.len() - 1)
.saturating_add(1),
);
let mut out = vec![C::ZERO; shift + q_len];
let out_q = &mut out[shift..];
match (q_len, e) {
(1, _) => out_q[0] = q[0].pow_ref(e),
(_, 1) => out_q.clone_from_slice(&q[..q_len]),
(_, 2) => square_truncated_to_out(out_q, q),
_ => pow_truncated_to_out(out_q, q, e),
}
trim_coefficients(&mut out);
out
}}
pub(crate) fn pow_truncated_assign_vec<C: PolynomialCoefficient + PowAssign<u64>>(
xs: &mut Vec<C>,
e: u64,
len: u64,
) {
match (xs.len(), e, len) {
(_, _, 0) => xs.clear(),
(0, 0, _) => xs.push(C::ONE),
(_, 0, _) => {
xs.truncate(1);
xs[0] = C::ONE;
}
(0, _, _) => {}
(_, 1, _) => truncate_coefficients(xs, len),
(1, _, _) => xs[0].pow_assign(e),
_ => *xs = pow_truncated_ref(xs, e, len),
}
}
impl PowTruncated for IntegerPolynomial {
type Output = Self;
#[inline]
fn pow_truncated(mut self, exp: u64, len: u64) -> Self {
self.pow_truncated_assign(exp, len);
self
}
}
impl PowTruncated for &IntegerPolynomial {
type Output = IntegerPolynomial;
#[inline]
fn pow_truncated(self, exp: u64, len: u64) -> IntegerPolynomial {
IntegerPolynomial {
coefficients: pow_truncated_ref(&self.coefficients, exp, len),
}
}
}
impl PowTruncatedAssign for IntegerPolynomial {
#[inline]
fn pow_truncated_assign(&mut self, exp: u64, len: u64) {
pow_truncated_assign_vec(&mut self.coefficients, exp, len);
}
}