use crate::ring::Ring;
use alloc::vec::Vec;
use core::fmt;
use core::ops::Div;
const POLY_KARATSUBA_THRESHOLD: usize = 24;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Poly<T> {
coeffs: Vec<T>,
}
fn top_nonzero<T: Ring>(v: &[T]) -> Option<usize> {
v.iter().rposition(|c| !c.is_zero())
}
impl<T: Ring> 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, c.zero());
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 {
match self.coeffs.get(i) {
Some(c) => c.clone(),
None => self
.coeffs
.first()
.expect("Poly::coeff: cannot derive a zero from the zero polynomial")
.zero(),
}
}
pub fn leading(&self) -> Option<&T> {
self.coeffs.last()
}
}
impl<T: Ring> Poly<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 {
match (self.coeffs.get(i), rhs.coeffs.get(i)) {
(Some(a), Some(b)) => out.push(a.clone() + b.clone()),
(Some(a), None) => out.push(a.clone()),
(None, Some(b)) => out.push(b.clone()),
(None, None) => unreachable!("i < max(len) so one side is in range"),
}
}
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 {
match (self.coeffs.get(i), rhs.coeffs.get(i)) {
(Some(a), Some(b)) => out.push(a.clone() - b.clone()),
(Some(a), None) => out.push(a.clone()),
(None, Some(b)) => out.push(-b.clone()),
(None, None) => unreachable!("i < max(len) so one side is in range"),
}
}
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 zero = self.coeffs[0].zero();
let mut out = alloc::vec![zero; 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, self.coeffs[0].zero());
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 = x.zero();
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 = c.zero();
for _ in 0..i {
acc = acc + c.clone();
}
out.push(acc);
}
Poly::new(out)
}
}
impl<T: Ring> Poly<T> {
pub fn neg(&self) -> Poly<T> {
Poly {
coeffs: self.coeffs.iter().map(|c| -c.clone()).collect(),
}
}
}
impl<T> Poly<T>
where
T: Ring + 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![lead.zero(); 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 + Ring> 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.is_zero() {
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: Ring> core::ops::$tr for Poly<T> {
type Output = Poly<T>;
#[inline]
fn $m(self, rhs: Poly<T>) -> Poly<T> {
Poly::$m(&self, &rhs)
}
}
impl<T: Ring> core::ops::$tr<&Poly<T>> for &Poly<T> {
type Output = Poly<T>;
#[inline]
fn $m(self, rhs: &Poly<T>) -> Poly<T> {
Poly::$m(self, rhs)
}
}
impl<T: Ring> core::ops::$atr for Poly<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")]
fn ip_deg(p: &[crate::int::Int]) -> Option<usize> {
p.iter().rposition(|c| !c.is_zero())
}
#[cfg(feature = "rational")]
fn ip_trim(mut p: alloc::vec::Vec<crate::int::Int>) -> alloc::vec::Vec<crate::int::Int> {
match ip_deg(&p) {
Some(d) => p.truncate(d + 1),
None => p.clear(),
}
p
}
#[cfg(feature = "rational")]
fn ip_lead(p: &[crate::int::Int]) -> &crate::int::Int {
&p[ip_deg(p).expect("ip_lead: zero polynomial")]
}
#[cfg(feature = "rational")]
fn ip_content(p: &[crate::int::Int]) -> crate::int::Int {
let mut g = crate::int::Int::ZERO;
for c in p {
g = g.gcd(c);
}
g
}
#[cfg(feature = "rational")]
fn ip_primitive(p: &[crate::int::Int]) -> alloc::vec::Vec<crate::int::Int> {
let c = ip_content(p);
if c.is_zero() || c.is_one() {
return p.to_vec();
}
p.iter().map(|x| x.div_exact(&c)).collect()
}
#[cfg(feature = "rational")]
fn ip_prem(a: &[crate::int::Int], b: &[crate::int::Int]) -> alloc::vec::Vec<crate::int::Int> {
use crate::int::Int;
let n = ip_deg(b).expect("ip_prem: division by zero polynomial");
let m = match ip_deg(a) {
Some(m) if m >= n => m,
_ => return a.to_vec(), };
let l = ip_lead(b).clone();
let mut r = a.to_vec();
for k in (0..=(m - n)).rev() {
if ip_deg(&r) == Some(n + k) {
let coef = r[n + k].clone(); for c in r.iter_mut() {
*c = Int::mul(c, &l);
}
for (i, bc) in b.iter().enumerate() {
r[k + i] = Int::sub(&r[k + i], &Int::mul(&coef, bc));
}
} else {
for c in r.iter_mut() {
*c = Int::mul(c, &l);
}
}
r = ip_trim(r);
}
r
}
#[cfg(feature = "rational")]
fn rational_to_primitive_int(p: &Poly<Rational>) -> alloc::vec::Vec<crate::int::Int> {
use crate::int::Int;
if p.is_zero() {
return alloc::vec::Vec::new();
}
let mut l = Int::ONE;
for c in p.coeffs() {
let d = c.denominator();
let g = l.gcd(d);
l = l.div_exact(&g).mul(d);
}
let ints: alloc::vec::Vec<Int> = p
.coeffs()
.iter()
.map(|c| c.numerator().mul(&l.div_exact(c.denominator())))
.collect();
ip_primitive(&ints)
}
#[cfg(feature = "rational")]
fn ip_sturm_chain(
p0: alloc::vec::Vec<crate::int::Int>,
p1: alloc::vec::Vec<crate::int::Int>,
) -> alloc::vec::Vec<alloc::vec::Vec<crate::int::Int>> {
use crate::int::Int;
let mut chain = alloc::vec![p0];
if ip_deg(&p1).is_none() {
return chain; }
chain.push(p1);
let mut g = Int::ONE;
let mut h = Int::ONE;
loop {
let last = chain.len() - 1;
let dega = ip_deg(&chain[last - 1]).expect("sturm: zero in chain");
let degb = ip_deg(&chain[last]).expect("sturm: zero in chain");
let e = dega - degb; let praw = ip_prem(&chain[last - 1], &chain[last]);
if ip_deg(&praw).is_none() {
break; }
let lead_sign = ip_lead(&chain[last]).signum();
let pow_sign = if (e + 1).is_multiple_of(2) {
1
} else {
lead_sign
};
let flip = -pow_sign < 0; let denom = g.mul(&h.pow(e as u32));
let mut next: alloc::vec::Vec<Int> = praw.iter().map(|c| c.div_exact(&denom)).collect();
if flip {
for c in next.iter_mut() {
*c = Int::neg(c);
}
}
let new_g = ip_lead(&chain[last]).abs();
h = if e == 0 {
h
} else {
new_g.pow(e as u32).div_exact(&h.pow((e - 1) as u32))
};
g = new_g;
chain.push(ip_trim(next));
}
chain
}
#[cfg(feature = "rational")]
fn ip_subresultant_gcd(
a: &[crate::int::Int],
b: &[crate::int::Int],
) -> alloc::vec::Vec<crate::int::Int> {
use crate::int::Int;
let mut a = ip_primitive(a);
let mut b = ip_primitive(b);
if ip_deg(&b).is_none() {
return a;
}
if ip_deg(&a).is_none() {
return b;
}
if ip_deg(&a) < ip_deg(&b) {
core::mem::swap(&mut a, &mut b);
}
let mut g = Int::ONE;
let mut h = Int::ONE;
loop {
let e = ip_deg(&a).unwrap() - ip_deg(&b).unwrap();
let r = ip_prem(&a, &b);
match ip_deg(&r) {
None => break, Some(0) => return alloc::vec![Int::ONE], Some(_) => {}
}
let denom = g.mul(&h.pow(e as u32));
let next: alloc::vec::Vec<Int> = r.iter().map(|c| c.div_exact(&denom)).collect();
let new_g = ip_lead(&b).clone();
h = if e == 0 {
h
} else {
new_g.pow(e as u32).div_exact(&h.pow((e - 1) as u32))
};
g = new_g;
a = b;
b = ip_trim(next);
}
ip_primitive(&b)
}
#[cfg(feature = "rational")]
fn int_poly_to_rational(p: &[crate::int::Int]) -> Poly<Rational> {
Poly::new(
p.iter()
.map(|c| Rational::from_integer(c.clone()))
.collect(),
)
}
#[cfg(feature = "rational")]
impl Poly<Rational> {
pub fn subresultant_gcd(&self, other: &Poly<Rational>) -> Poly<Rational> {
if self.is_zero() {
return other.monic();
}
if other.is_zero() {
return self.monic();
}
let a = rational_to_primitive_int(self);
let b = rational_to_primitive_int(other);
let g = ip_subresultant_gcd(&a, &b);
int_poly_to_rational(&g).monic()
}
pub fn squarefree_part(&self) -> Poly<Rational> {
if self.degree().unwrap_or(0) < 1 {
return self.monic();
}
let g = self.subresultant_gcd(&self.derivative());
self.div_rem(&g).0.monic()
}
pub fn sturm_chain(&self) -> alloc::vec::Vec<Poly<Rational>> {
let p0 = rational_to_primitive_int(self);
if ip_deg(&p0).is_none() {
return alloc::vec![Poly::zero()];
}
let p1 = rational_to_primitive_int(&self.derivative());
ip_sturm_chain(p0, p1)
.iter()
.map(|r| int_poly_to_rational(r))
.collect()
}
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()
}
}
#[cfg(all(feature = "poly", feature = "rational"))]
impl Poly<crate::rational::Rational> {
pub fn factor(&self) -> alloc::vec::Vec<(Poly<crate::rational::Rational>, usize)> {
crate::poly_factor::factor_rational(self)
}
}