use crate::integer_polynomial::arithmetic::pow::pow_ref;
use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
use crate::natural::Natural;
use crate::natural_polynomial::NaturalPolynomial;
use crate::natural_polynomial::arithmetic::mod_power_of_2_mul::mod_power_of_2_mul_ref_ref;
use crate::natural_polynomial::arithmetic::mod_power_of_2_square::{
assert_reduced, mod_power_of_2_square_ref,
};
use alloc::vec;
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, ModPowerOf2Assign, ModPowerOf2Pow, ModPowerOf2PowAssign,
};
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::polynomial::{Polynomial, pow_binexp_trimmed};
pub(crate) fn power_needs_no_reduction(len: usize, bits: u64, e: u64, pow: u64) -> bool {
(bits + u64::exact_from(len).ceiling_log_base_2())
.checked_mul(e)
.is_some_and(|b| b <= pow)
}
crate_test_fn! {mod_power_of_2_pow_binexp(xs: &[Natural], e: u64, pow: u64) -> Vec<Natural> {
pow_binexp_trimmed(
xs,
e,
|r| mod_power_of_2_square_ref(r, pow).into_coefficients_asc(),
|r, xs| mod_power_of_2_mul_ref_ref(r, xs, pow).into_coefficients_asc(),
)
}}
crate_test_fn! {mod_power_of_2_pow_exact(xs: &[Natural], e: u64, pow: u64) -> Vec<Natural> {
let mut out = pow_ref(xs, e);
for x in &mut out {
x.mod_power_of_2_assign(pow);
}
while out.last() == Some(&Natural::ZERO) {
out.pop();
}
out
}}
pub(crate) fn mod_power_of_2_pow_ref(xs: &[Natural], e: u64, pow: u64) -> NaturalPolynomial {
if pow == 0 {
return NaturalPolynomial::ZERO;
}
if e == 0 {
return NaturalPolynomial::one();
}
let Some(low) = xs.iter().position(|x| *x != 0u32) else {
return NaturalPolynomial::ZERO;
};
let q = &xs[low..];
let mut power = match (q.len(), e) {
(1, _) => {
let c = (&q[0]).mod_power_of_2_pow(Natural::from(e), pow);
if c == 0u32 { Vec::new() } else { vec![c] }
}
(_, 1) => q.to_vec(),
_ if power_needs_no_reduction(q.len(), vec_max_bits(q).0, e, pow) => pow_ref(q, e),
(_, 2) => mod_power_of_2_square_ref(q, pow).into_coefficients_asc(),
_ => mod_power_of_2_pow_binexp(q, e, pow),
};
if power.is_empty() {
return NaturalPolynomial::ZERO;
}
if low != 0 {
let shift = usize::exact_from(e)
.checked_mul(low)
.expect("the power has too many coefficients to represent");
power.splice(0..0, core::iter::repeat_n(Natural::ZERO, shift));
}
NaturalPolynomial {
coefficients: power,
}
}
impl ModPowerOf2Pow<u64> for NaturalPolynomial {
type Output = Self;
#[inline]
fn mod_power_of_2_pow(mut self, exp: u64, pow: u64) -> Self {
self.mod_power_of_2_pow_assign(exp, pow);
self
}
}
impl ModPowerOf2Pow<u64> for &NaturalPolynomial {
type Output = NaturalPolynomial;
fn mod_power_of_2_pow(self, exp: u64, pow: u64) -> NaturalPolynomial {
assert_reduced(self, pow);
mod_power_of_2_pow_ref(&self.coefficients, exp, pow)
}
}
impl ModPowerOf2PowAssign<u64> for NaturalPolynomial {
fn mod_power_of_2_pow_assign(&mut self, exp: u64, pow: u64) {
assert_reduced(self, pow);
let xs = &mut self.coefficients;
match (xs.len(), exp, pow) {
(_, _, 0) => xs.clear(),
(0, 0, _) => xs.push(Natural::ONE),
(_, 0, _) => {
xs.truncate(1);
xs[0] = Natural::ONE;
}
(0, _, _) | (_, 1, _) => {}
(1, _, _) => {
xs[0].mod_power_of_2_pow_assign(Natural::from(exp), pow);
if xs[0] == 0u32 {
xs.clear();
}
}
_ => *self = mod_power_of_2_pow_ref(xs, exp, pow),
}
}
}