use crate::Rational;
use crate::rational_polynomial::RationalPolynomial;
use alloc::borrow::Cow;
use alloc::vec;
use alloc::vec::Vec;
use core::cmp::max;
use malachite_base::num::arithmetic::traits::{
AddMul, AddMulAssign, CoprimeWith, Square, UnsignedAbs,
};
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::{BitIterable, SignificantBits};
use malachite_base::polynomial::{Evaluate, EvaluateMany};
use malachite_nz::integer::Integer;
use malachite_nz::integer_polynomial::IntegerPolynomial;
use malachite_nz::integer_polynomial::arithmetic::evaluate::divide_and_conquer_blocks;
use malachite_nz::natural::Natural;
use malachite_nz::platform::Limb;
fn from_homogeneous(n: Integer, b_power: Integer, leading: &Integer, b: &Integer) -> Rational {
if leading
.unsigned_abs_ref()
.coprime_with(b.unsigned_abs_ref())
{
Rational {
sign: n >= 0u32,
numerator: n.unsigned_abs(),
denominator: b_power.unsigned_abs(),
}
} else {
Rational::from_integers(n, b_power)
}
}
fn homogeneous_horner(f: &[Integer], a: &Integer, b: &Integer) -> (Integer, Integer) {
let (leading, rest) = f.split_last().unwrap();
let mut n = leading.clone();
let mut b_power = b.clone();
let mut first = true;
for c in rest.iter().rev() {
if first {
first = false;
} else {
b_power *= b;
}
n *= a;
n.add_mul_assign(c, &b_power);
}
(n, b_power)
}
fn homogeneous_divide_and_conquer(f: &[Integer], a: Integer, b: Integer) -> (Integer, Integer) {
let len = f.len();
let h = usize::exact_from((len - 1).significant_bits());
let mut powers = vec![Integer::ZERO; h << 1];
let (a_powers, b_powers) = powers.split_at_mut(h);
a_powers[0] = a;
b_powers[0] = b;
for k in 1..h {
a_powers[k] = (&a_powers[k - 1]).square();
b_powers[k] = (&b_powers[k - 1]).square();
}
let (a_powers, b_powers) = (&*a_powers, &*b_powers);
let b_power = |l: usize| -> Cow<'_, Integer> {
if l.is_power_of_two() {
Cow::Borrowed(&b_powers[usize::exact_from(l.trailing_zeros())])
} else {
let mut power = Integer::ONE;
for (k, bit) in l.bits().enumerate() {
if bit {
power *= &b_powers[k];
}
}
Cow::Owned(power)
}
};
let n = divide_and_conquer_blocks(
f,
|low, high| (low * &b_powers[0]).add_mul(high, &a_powers[0]),
Integer::clone,
|lower, upper, k, upper_len| (upper * &a_powers[k]).add_mul(lower, &*b_power(upper_len)),
);
(n, b_power(len - 1).into_owned())
}
fn to_integers(x: &Rational) -> (Integer, Integer) {
(
Integer::from_sign_and_abs_ref(*x >= 0u32, x.numerator_ref()),
Integer::from(x.denominator_ref()),
)
}
crate_test_fn! {evaluate_integer_polynomial_horner(f: &[Integer], x: &Rational) -> Rational {
match f.len() {
0 => Rational::ZERO,
1 => Rational::from(&f[0]),
_ => {
let (a, b) = to_integers(x);
let (n, b_power) = homogeneous_horner(f, &a, &b);
from_homogeneous(n, b_power, f.last().unwrap(), &b)
}
}
}}
crate_test_fn! {evaluate_integer_polynomial_divide_and_conquer(
f: &[Integer],
x: &Rational,
) -> Rational {
match f.len() {
0 => Rational::ZERO,
1 => Rational::from(&f[0]),
_ => {
let (a, b) = to_integers(x);
let b_copy = b.clone();
let (n, b_power) = homogeneous_divide_and_conquer(f, a, b);
from_homogeneous(n, b_power, f.last().unwrap(), &b_copy)
}
}
}}
pub(crate) const EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_ONE_LIMB_THRESHOLD: u64 = 16384;
pub(crate) const EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_LENGTH_TIMES_LIMBS_THRESHOLD: u64 = 128;
pub(crate) const EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_MIN_LENGTH: u64 = 4;
fn evaluate_rational_use_divide_and_conquer(
len: usize,
leading_bits: u64,
a_bits: u64,
b_bits: u64,
) -> bool {
let len = u64::exact_from(len);
let point_limbs = max(a_bits, b_bits).div_ceil(Limb::WIDTH);
if point_limbs <= 1 {
len.saturating_mul(leading_bits.div_ceil(Limb::WIDTH) + 1)
.saturating_mul(b_bits)
>= EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_ONE_LIMB_THRESHOLD
} else {
len >= EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_MIN_LENGTH
&& len.saturating_mul(point_limbs)
>= EVALUATE_RATIONAL_DIVIDE_AND_CONQUER_LENGTH_TIMES_LIMBS_THRESHOLD
}
}
fn evaluate_integer_polynomial(p: &IntegerPolynomial, a: Integer, b: Integer) -> Rational {
if b == 1u32 {
return Rational::from(p.evaluate(a));
}
let f = p.coefficients_asc();
let len = f.len();
match len {
0 => return Rational::ZERO,
1 => return Rational::from(&f[0]),
_ => {}
}
let leading = f.last().unwrap();
if evaluate_rational_use_divide_and_conquer(
len,
leading.significant_bits(),
a.significant_bits(),
b.significant_bits(),
) {
let b_copy = b.clone();
let (n, b_power) = homogeneous_divide_and_conquer(f, a, b);
from_homogeneous(n, b_power, leading, &b_copy)
} else {
let (n, b_power) = homogeneous_horner(f, &a, &b);
from_homogeneous(n, b_power, leading, &b)
}
}
impl Evaluate<&Rational> for &IntegerPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: &Rational) -> Rational {
let (a, b) = to_integers(x);
evaluate_integer_polynomial(self, a, b)
}
}
impl Evaluate<Rational> for &IntegerPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: Rational) -> Rational {
let sign = x >= 0u32;
let (numerator, denominator) = x.into_numerator_and_denominator();
evaluate_integer_polynomial(
self,
Integer::from_sign_and_abs(sign, numerator),
Integer::from(denominator),
)
}
}
fn divide_by_denominator(value: Rational, denominator: &Natural) -> Rational {
if *denominator == 1u32 {
value
} else {
value / Rational::from(denominator)
}
}
impl Evaluate<&Rational> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: &Rational) -> Rational {
divide_by_denominator((&self.numerator).evaluate(x), &self.denominator)
}
}
impl Evaluate<Rational> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: Rational) -> Rational {
divide_by_denominator((&self.numerator).evaluate(x), &self.denominator)
}
}
fn integer_value_over_denominator(value: Integer, denominator: &Natural) -> Rational {
if *denominator == 1u32 {
Rational::from(value)
} else {
Rational::from_integers(value, Integer::from(denominator))
}
}
impl Evaluate<&Integer> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: &Integer) -> Rational {
integer_value_over_denominator((&self.numerator).evaluate(x), &self.denominator)
}
}
impl Evaluate<Integer> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate(self, x: Integer) -> Rational {
integer_value_over_denominator((&self.numerator).evaluate(x), &self.denominator)
}
}
impl EvaluateMany<Rational> for &IntegerPolynomial {
type Output = Rational;
#[inline]
fn evaluate_many(self, xs: &[Rational]) -> Vec<Rational> {
xs.iter().map(|x| self.evaluate(x)).collect()
}
}
impl EvaluateMany<Rational> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate_many(self, xs: &[Rational]) -> Vec<Rational> {
xs.iter().map(|x| self.evaluate(x)).collect()
}
}
impl EvaluateMany<Integer> for &RationalPolynomial {
type Output = Rational;
#[inline]
fn evaluate_many(self, xs: &[Integer]) -> Vec<Rational> {
xs.iter().map(|x| self.evaluate(x)).collect()
}
}