use alloc::vec::Vec;
use core::fmt;
use core::ops::{Add, Div, Mul, Neg, Sub};
const POLY_KARATSUBA_THRESHOLD: usize = 24;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Poly<T> {
coeffs: Vec<T>,
}
fn top_nonzero<T: Default + PartialEq>(v: &[T]) -> Option<usize> {
v.iter().rposition(|c| *c != T::default())
}
impl<T: Clone + Default + PartialEq> Poly<T> {
pub fn new(mut coeffs: Vec<T>) -> Poly<T> {
match top_nonzero(&coeffs) {
Some(i) => coeffs.truncate(i + 1),
None => coeffs.clear(),
}
Poly { coeffs }
}
pub fn zero() -> Poly<T> {
Poly { coeffs: Vec::new() }
}
pub fn constant(c: T) -> Poly<T> {
Poly::new(alloc::vec![c])
}
pub fn monomial(c: T, degree: usize) -> Poly<T> {
let mut v = Vec::with_capacity(degree + 1);
v.resize(degree, T::default());
v.push(c);
Poly::new(v)
}
#[inline]
pub fn coeffs(&self) -> &[T] {
&self.coeffs
}
#[inline]
pub fn is_zero(&self) -> bool {
self.coeffs.is_empty()
}
#[inline]
pub fn degree(&self) -> Option<usize> {
self.coeffs.len().checked_sub(1)
}
pub fn coeff(&self, i: usize) -> T {
self.coeffs.get(i).cloned().unwrap_or_default()
}
pub fn leading(&self) -> Option<&T> {
self.coeffs.last()
}
}
impl<T> Poly<T>
where
T: Clone + Default + PartialEq + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
pub fn add(&self, rhs: &Poly<T>) -> Poly<T> {
let n = self.coeffs.len().max(rhs.coeffs.len());
let mut out = Vec::with_capacity(n);
for i in 0..n {
out.push(self.coeff(i) + rhs.coeff(i));
}
Poly::new(out)
}
pub fn sub(&self, rhs: &Poly<T>) -> Poly<T> {
let n = self.coeffs.len().max(rhs.coeffs.len());
let mut out = Vec::with_capacity(n);
for i in 0..n {
out.push(self.coeff(i) - rhs.coeff(i));
}
Poly::new(out)
}
pub fn mul(&self, rhs: &Poly<T>) -> Poly<T> {
if self.is_zero() || rhs.is_zero() {
return Poly::zero();
}
if self.coeffs.len().min(rhs.coeffs.len()) < POLY_KARATSUBA_THRESHOLD {
return self.mul_schoolbook(rhs);
}
let m = self.coeffs.len().max(rhs.coeffs.len()) / 2;
let (a0, a1) = self.split_at(m);
let (b0, b1) = rhs.split_at(m);
let z0 = Poly::mul(&a0, &b0);
let z2 = Poly::mul(&a1, &b1);
let mid = Poly::mul(&Poly::add(&a0, &a1), &Poly::add(&b0, &b1));
let z1 = Poly::sub(&Poly::sub(&mid, &z0), &z2);
let r = Poly::add(&z0, &z1.shift_up(m));
Poly::add(&r, &z2.shift_up(2 * m))
}
fn mul_schoolbook(&self, rhs: &Poly<T>) -> Poly<T> {
let mut out = alloc::vec![T::default(); self.coeffs.len() + rhs.coeffs.len() - 1];
for (i, a) in self.coeffs.iter().enumerate() {
for (j, b) in rhs.coeffs.iter().enumerate() {
let prod = a.clone() * b.clone();
out[i + j] = out[i + j].clone() + prod;
}
}
Poly::new(out)
}
fn split_at(&self, k: usize) -> (Poly<T>, Poly<T>) {
if k >= self.coeffs.len() {
return (self.clone(), Poly::zero());
}
(
Poly::new(self.coeffs[..k].to_vec()),
Poly::new(self.coeffs[k..].to_vec()),
)
}
fn shift_up(&self, k: usize) -> Poly<T> {
if self.is_zero() || k == 0 {
return self.clone();
}
let mut v = Vec::with_capacity(self.coeffs.len() + k);
v.resize(k, T::default());
v.extend_from_slice(&self.coeffs);
Poly::new(v)
}
pub fn scalar_mul(&self, scalar: &T) -> Poly<T> {
Poly::new(
self.coeffs
.iter()
.map(|c| c.clone() * scalar.clone())
.collect(),
)
}
pub fn eval(&self, x: &T) -> T {
let mut acc = T::default();
for c in self.coeffs.iter().rev() {
acc = acc * x.clone() + c.clone();
}
acc
}
pub fn derivative(&self) -> Poly<T> {
if self.coeffs.len() < 2 {
return Poly::zero();
}
let mut out = Vec::with_capacity(self.coeffs.len() - 1);
for (i, c) in self.coeffs.iter().enumerate().skip(1) {
let mut acc = T::default();
for _ in 0..i {
acc = acc + c.clone();
}
out.push(acc);
}
Poly::new(out)
}
}
impl<T> Poly<T>
where
T: Clone
+ Default
+ PartialEq
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Neg<Output = T>,
{
pub fn neg(&self) -> Poly<T> {
Poly {
coeffs: self.coeffs.iter().map(|c| -c.clone()).collect(),
}
}
}
impl<T> Poly<T>
where
T: Clone
+ Default
+ PartialEq
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>,
{
pub fn div_rem(&self, divisor: &Poly<T>) -> (Poly<T>, Poly<T>) {
let dd = divisor
.degree()
.expect("Poly::div_rem: division by zero polynomial");
let lead = divisor.leading().unwrap().clone();
let mut rem = self.coeffs.clone();
let mut quot = alloc::vec![T::default(); self.coeffs.len().saturating_sub(dd)];
while let Some(rd) = top_nonzero(&rem) {
if rd < dd {
break;
}
let coef = rem[rd].clone() / lead.clone();
let shift = rd - dd;
for (i, dc) in divisor.coeffs.iter().enumerate() {
rem[shift + i] = rem[shift + i].clone() - coef.clone() * dc.clone();
}
quot[shift] = coef;
}
(Poly::new(quot), Poly::new(rem))
}
pub fn rem(&self, divisor: &Poly<T>) -> Poly<T> {
self.div_rem(divisor).1
}
pub fn monic(&self) -> Poly<T> {
match self.leading() {
None => Poly::zero(),
Some(lead) => {
let inv_lead = lead.clone();
Poly::new(
self.coeffs
.iter()
.map(|c| c.clone() / inv_lead.clone())
.collect(),
)
}
}
}
pub fn gcd(&self, other: &Poly<T>) -> Poly<T> {
let mut a = self.clone();
let mut b = other.clone();
while !b.is_zero() {
let r = a.rem(&b);
a = b;
b = r;
}
a.monic()
}
}
impl<T: fmt::Display + Clone + Default + PartialEq> fmt::Display for Poly<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return f.write_str("0");
}
let mut first = true;
for (i, c) in self.coeffs.iter().enumerate().rev() {
if *c == T::default() {
continue;
}
if !first {
f.write_str(" + ")?;
}
first = false;
match i {
0 => write!(f, "{c}")?,
1 => write!(f, "{c}·x")?,
_ => write!(f, "{c}·x^{i}")?,
}
}
Ok(())
}
}
macro_rules! poly_binop {
($tr:ident, $m:ident, $atr:ident, $am:ident) => {
impl<T> core::ops::$tr for Poly<T>
where
T: Clone + Default + PartialEq + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
type Output = Poly<T>;
#[inline]
fn $m(self, rhs: Poly<T>) -> Poly<T> {
Poly::$m(&self, &rhs)
}
}
impl<T> core::ops::$tr<&Poly<T>> for &Poly<T>
where
T: Clone + Default + PartialEq + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
type Output = Poly<T>;
#[inline]
fn $m(self, rhs: &Poly<T>) -> Poly<T> {
Poly::$m(self, rhs)
}
}
impl<T> core::ops::$atr for Poly<T>
where
T: Clone + Default + PartialEq + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
#[inline]
fn $am(&mut self, rhs: Poly<T>) {
*self = Poly::$m(self, &rhs);
}
}
};
}
poly_binop!(Add, add, AddAssign, add_assign);
poly_binop!(Sub, sub, SubAssign, sub_assign);
poly_binop!(Mul, mul, MulAssign, mul_assign);
#[cfg(feature = "rational")]
use crate::rational::Rational;
#[cfg(feature = "rational")]
pub fn sturm_variations(chain: &[Poly<Rational>], x: &Rational) -> usize {
let mut last = 0i32;
let mut count = 0;
for p in chain {
let s = p.eval(x).signum();
if s != 0 {
if last != 0 && s != last {
count += 1;
}
last = s;
}
}
count
}
#[cfg(feature = "rational")]
pub fn sturm_count(chain: &[Poly<Rational>], lo: &Rational, hi: &Rational) -> usize {
sturm_variations(chain, lo).saturating_sub(sturm_variations(chain, hi))
}
#[cfg(feature = "rational")]
impl Poly<Rational> {
pub fn squarefree_part(&self) -> Poly<Rational> {
if self.degree().unwrap_or(0) < 1 {
return self.monic();
}
let g = self.gcd(&self.derivative());
self.div_rem(&g).0.monic()
}
pub fn sturm_chain(&self) -> alloc::vec::Vec<Poly<Rational>> {
let mut chain = alloc::vec![self.clone(), self.derivative()];
while !chain.last().unwrap().is_zero() {
let n = chain.len();
let r = chain[n - 2].rem(&chain[n - 1]);
if r.is_zero() {
break;
}
chain.push(r.neg());
}
chain
}
fn real_root_bound(&self) -> Rational {
let lead = match self.leading() {
Some(c) => c.abs(),
None => return Rational::ONE,
};
let mut m = Rational::ZERO;
let deg = self.degree().unwrap_or(0);
for i in 0..deg {
let r = Rational::div(&self.coeff(i).abs(), &lead);
if r > m {
m = r;
}
}
Rational::add(&m, &Rational::ONE)
}
pub fn count_real_roots_in(&self, lo: &Rational, hi: &Rational) -> usize {
let sf = self.squarefree_part();
if sf.degree().unwrap_or(0) < 1 {
return 0;
}
sturm_count(&sf.sturm_chain(), lo, hi)
}
pub fn real_root_count(&self) -> usize {
let sf = self.squarefree_part();
if sf.degree().unwrap_or(0) < 1 {
return 0;
}
let b = sf.real_root_bound();
sturm_count(&sf.sturm_chain(), &Rational::neg(&b), &b)
}
pub fn isolate_real_roots(&self) -> alloc::vec::Vec<(Rational, Rational)> {
let mut out = alloc::vec::Vec::new();
let sf = self.squarefree_part();
if sf.degree().unwrap_or(0) < 1 {
return out;
}
let chain = sf.sturm_chain();
let two = Rational::from_integer(crate::int::Int::from_i64(2));
let b = sf.real_root_bound();
let neg_b = Rational::neg(&b);
let mut stack = alloc::vec![(neg_b, b)];
while let Some((lo, hi)) = stack.pop() {
let c = sturm_count(&chain, &lo, &hi);
if c == 0 {
continue;
}
if c == 1 {
out.push((lo, hi));
continue;
}
let mid = Rational::div(&Rational::add(&lo, &hi), &two);
stack.push((lo, mid.clone()));
stack.push((mid, hi));
}
out.sort_by(|a, b| a.0.cmp(&b.0));
out
}
}
#[cfg(all(feature = "rational", feature = "float"))]
impl Poly<Rational> {
pub fn real_roots(
&self,
precision: u64,
mode: crate::float::RoundingMode,
) -> alloc::vec::Vec<crate::float::Float> {
use crate::float::Float;
let sf = self.squarefree_part();
let two = Rational::from_integer(crate::int::Int::from_i64(2));
self.isolate_real_roots()
.into_iter()
.map(|(mut lo, mut hi)| {
for _ in 0..(precision + 64) {
if lo == hi {
break;
}
let flo = Float::from_rational(&lo, precision, mode);
let fhi = Float::from_rational(&hi, precision, mode);
if flo == fhi {
return flo;
}
let mid = Rational::div(&Rational::add(&lo, &hi), &two);
let sm = sf.eval(&mid).signum();
if sm == 0 {
lo = mid.clone();
hi = mid;
} else if sm == sf.eval(&hi).signum() {
hi = mid;
} else {
lo = mid;
}
}
Float::from_rational(
&Rational::div(&Rational::add(&lo, &hi), &two),
precision,
mode,
)
})
.collect()
}
}