use crate::integer::Integer;
use crate::integer_polynomial::IntegerPolynomial;
use crate::natural::Natural;
use crate::platform::Limb;
use alloc::vec::Vec;
use core::iter::Sum;
use core::mem::replace;
use core::ops::{AddAssign, Mul, MulAssign};
use malachite_base::num::arithmetic::traits::{Parity, PowerOf2, Square};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::{BitAccess, SignificantBits};
use malachite_base::polynomial::{Evaluate, EvaluateMany, ModEvaluate};
use malachite_base::unsigned_polynomial::arithmetic::evaluate::mod_evaluate_slice;
crate_test_fn! {evaluate_horner<T>(coefficients: &[T], x: &T) -> T
where
T: Clone + Zero + PartialEq + for<'a> AddAssign<&'a T> + for<'a> MulAssign<&'a T>,
{
let Some((leading, rest)) = coefficients.split_last() else {
return T::ZERO;
};
if rest.is_empty() || *x == T::ZERO {
return coefficients[0].clone();
}
let mut value = leading.clone();
for c in rest.iter().rev() {
value *= x;
value += c;
}
value
}}
#[doc(hidden)]
pub fn divide_and_conquer_blocks<C, T: Clone + Zero>(
coefficients: &[C],
pair: impl Fn(&C, &C) -> T,
single: impl Fn(&C) -> T,
merge: impl Fn(&T, T, usize, usize) -> T,
) -> T {
let len = coefficients.len();
assert!(len >= 2);
let h = usize::exact_from((len - 1).significant_bits());
let mut partials = vec![T::ZERO; h + 1];
let absorb =
|mut block: T, mut block_len: usize, end: usize, partials: &mut [T]| -> (usize, usize) {
let carries = usize::exact_from(end.trailing_zeros());
let mut k = 1;
while k < carries {
block = merge(&partials[k], block, k, block_len);
block_len += usize::power_of_2(u64::exact_from(k));
k += 1;
}
partials[k] = block;
(k, block_len)
};
let mut k = 1;
let mut top_len = 0;
for (i, [low, high]) in coefficients.as_chunks::<2>().0.iter().enumerate() {
(k, top_len) = absorb(pair(low, high), 2, (i + 1) << 1, &mut partials);
}
if len.odd() {
(k, top_len) = absorb(single(&coefficients[len - 1]), 1, len + 1, &mut partials);
}
let mut value = replace(&mut partials[k], T::ZERO);
while k < h {
if (len - 1).get_bit(u64::exact_from(k)) {
value = merge(&partials[k], value, k, top_len);
top_len += usize::power_of_2(u64::exact_from(k));
}
k += 1;
}
value
}
crate_test_fn! {evaluate_divide_and_conquer<T>(coefficients: &[T], x: &T) -> T
where
T: Clone + Zero + for<'a> AddAssign<&'a T> + for<'a> MulAssign<&'a T>,
for<'a> &'a T: Mul<&'a T, Output = T> + Square<Output = T>,
{
match coefficients.len() {
0 => return T::ZERO,
1 => return coefficients[0].clone(),
_ => {}
}
let h = usize::exact_from((coefficients.len() - 1).significant_bits());
let mut powers: Vec<T> = Vec::with_capacity(h - 1);
for k in 1..h {
let square = if k == 1 {
x.square()
} else {
powers[k - 2].square()
};
powers.push(square);
}
let power = |k: usize| if k == 0 { x } else { &powers[k - 1] };
divide_and_conquer_blocks(
coefficients,
|low, high| {
let mut block = high * x;
block += low;
block
},
T::clone,
|lower, mut upper, k, _| {
upper *= power(k);
upper += lower;
upper
},
)
}}
pub(crate) const EVALUATE_DIVIDE_AND_CONQUER_ONE_LIMB_THRESHOLD: usize = 1024;
pub(crate) const EVALUATE_DIVIDE_AND_CONQUER_LENGTH_TIMES_LIMBS_THRESHOLD: usize = 256;
pub(crate) const EVALUATE_DIVIDE_AND_CONQUER_MIN_LENGTH: usize = 4;
pub(crate) fn evaluate_use_divide_and_conquer(len: usize, x_bits: u64) -> bool {
let x_limbs = usize::exact_from(x_bits.div_ceil(Limb::WIDTH));
if x_limbs <= 1 {
len >= EVALUATE_DIVIDE_AND_CONQUER_ONE_LIMB_THRESHOLD
} else {
len >= EVALUATE_DIVIDE_AND_CONQUER_MIN_LENGTH
&& len.saturating_mul(x_limbs)
>= EVALUATE_DIVIDE_AND_CONQUER_LENGTH_TIMES_LIMBS_THRESHOLD
}
}
pub(crate) fn evaluate<T>(coefficients: &[T], x: &T) -> T
where
T: Clone + Zero + One + PartialEq + for<'a> AddAssign<&'a T> + for<'a> MulAssign<&'a T>,
T: for<'a> Sum<&'a T>,
for<'a> &'a T: Mul<&'a T, Output = T> + Square<Output = T> + SignificantBits,
{
if *x == T::ONE {
coefficients.iter().sum()
} else if evaluate_use_divide_and_conquer(coefficients.len(), x.significant_bits()) {
evaluate_divide_and_conquer(coefficients, x)
} else {
evaluate_horner(coefficients, x)
}
}
fn evaluate_integer(coefficients: &[Integer], x: &Integer) -> Integer {
if *x == -1i32 {
coefficients.iter().step_by(2).sum::<Integer>()
- coefficients.iter().skip(1).step_by(2).sum::<Integer>()
} else {
evaluate(coefficients, x)
}
}
impl Evaluate<&Integer> for &IntegerPolynomial {
type Output = Integer;
#[inline]
fn evaluate(self, x: &Integer) -> Integer {
evaluate_integer(&self.coefficients, x)
}
}
impl Evaluate<Integer> for &IntegerPolynomial {
type Output = Integer;
#[inline]
fn evaluate(self, x: Integer) -> Integer {
evaluate_integer(&self.coefficients, &x)
}
}
fn integer_mod_u64(c: &Integer, m: u64, m_natural: &Natural) -> u64 {
let r = u64::exact_from(&(&c.abs % m_natural));
if c.sign || r == 0 { r } else { m - r }
}
impl ModEvaluate<u64> for &IntegerPolynomial {
type Output = u64;
fn mod_evaluate(self, x: u64, m: u64) -> u64 {
assert_ne!(m, 0, "m cannot be 0");
assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
let m_natural = Natural::from(m);
match self.coefficients.as_slice() {
[] => 0,
[c, ..] if x == 0 => integer_mod_u64(c, m, &m_natural),
coefficients => {
let reduced: Vec<u64> = coefficients
.iter()
.map(|c| integer_mod_u64(c, m, &m_natural))
.collect();
mod_evaluate_slice(&reduced, x, m)
}
}
}
}
impl EvaluateMany<Integer> for &IntegerPolynomial {
type Output = Integer;
#[inline]
fn evaluate_many(self, xs: &[Integer]) -> Vec<Integer> {
xs.iter().map(|x| self.evaluate(x)).collect()
}
}