#[allow(unused_imports)]
use crate::prelude::*;
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{Signed, Zero};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Polynomial {
pub coeffs: Vec<BigRational>,
}
impl Polynomial {
pub fn new(coeffs: Vec<BigRational>) -> Self {
let mut poly = Self { coeffs };
poly.normalize();
poly
}
fn normalize(&mut self) {
while self.coeffs.len() > 1 && self.coeffs.last().is_some_and(|c| c.is_zero()) {
self.coeffs.pop();
}
if self.coeffs.is_empty() {
self.coeffs.push(BigRational::zero());
}
}
pub fn degree(&self) -> usize {
if self.coeffs.len() == 1 && self.coeffs[0].is_zero() {
0
} else {
self.coeffs.len() - 1
}
}
pub fn eval(&self, x: &BigRational) -> BigRational {
let mut result = BigRational::zero();
for coeff in self.coeffs.iter().rev() {
result = result * x + coeff;
}
result
}
pub fn derivative(&self) -> Polynomial {
if self.degree() == 0 {
return Polynomial::new(vec![BigRational::zero()]);
}
let coeffs: Vec<_> = self
.coeffs
.iter()
.enumerate()
.skip(1)
.map(|(i, c)| c * BigRational::from_integer(BigInt::from(i)))
.collect();
Polynomial::new(coeffs)
}
pub fn remainder(&self, other: &Polynomial) -> Polynomial {
let mut rem = self.clone();
let divisor_deg = other.degree();
let divisor_lead = &other.coeffs[divisor_deg];
loop {
if rem.coeffs.len() == 1 && rem.coeffs[0].is_zero() {
break;
}
if rem.degree() < divisor_deg || rem.coeffs.is_empty() {
break;
}
let rem_deg = rem.degree();
let rem_lead = &rem.coeffs[rem_deg];
let factor = rem_lead / divisor_lead;
for i in 0..=divisor_deg {
rem.coeffs[rem_deg - divisor_deg + i] =
&rem.coeffs[rem_deg - divisor_deg + i] - &factor * &other.coeffs[i];
}
rem.normalize();
}
rem
}
}
#[derive(Debug, Clone)]
pub struct RootCountConfig {
pub use_sturm: bool,
pub use_descartes: bool,
pub use_budan: bool,
}
impl Default for RootCountConfig {
fn default() -> Self {
Self {
use_sturm: true,
use_descartes: true,
use_budan: false,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct RootCountStats {
pub roots_counted: u64,
pub sturm_sequences: u64,
pub descartes_variations: u64,
}
#[derive(Debug)]
pub struct RootCounter {
config: RootCountConfig,
stats: RootCountStats,
}
impl RootCounter {
pub fn new(config: RootCountConfig) -> Self {
Self {
config,
stats: RootCountStats::default(),
}
}
pub fn default_config() -> Self {
Self::new(RootCountConfig::default())
}
pub fn count_roots_sturm(
&mut self,
poly: &Polynomial,
a: &BigRational,
b: &BigRational,
) -> usize {
if !self.config.use_sturm {
return 0;
}
self.stats.sturm_sequences += 1;
let sturm_seq = self.sturm_sequence(poly);
let sign_changes_a = self.count_sign_changes(&sturm_seq, a);
let sign_changes_b = self.count_sign_changes(&sturm_seq, b);
(sign_changes_a as isize - sign_changes_b as isize).unsigned_abs()
}
fn sturm_sequence(&self, poly: &Polynomial) -> Vec<Polynomial> {
let mut seq = vec![poly.clone(), poly.derivative()];
loop {
let n = seq.len();
if seq[n - 1].degree() == 0 {
break;
}
let remainder = seq[n - 2].remainder(&seq[n - 1]);
let negated = Polynomial::new(remainder.coeffs.iter().map(|c| -c).collect());
if negated.degree() == 0 && negated.coeffs[0].is_zero() {
break;
}
seq.push(negated);
if seq.len() > 1000 {
break;
}
}
seq
}
fn count_sign_changes(&self, seq: &[Polynomial], x: &BigRational) -> usize {
let mut last_sign = 0i8;
let mut changes = 0;
for poly in seq {
let val = poly.eval(x);
let sign = if val.is_positive() {
1
} else if val.is_negative() {
-1
} else {
0
};
if sign != 0 && last_sign != 0 && sign != last_sign {
changes += 1;
}
if sign != 0 {
last_sign = sign;
}
}
changes
}
pub fn count_positive_roots_descartes(&mut self, poly: &Polynomial) -> usize {
if !self.config.use_descartes {
return 0;
}
self.stats.descartes_variations += 1;
let mut last_sign = 0i8;
let mut variations = 0;
for coeff in &poly.coeffs {
let sign = if coeff.is_positive() {
1
} else if coeff.is_negative() {
-1
} else {
0
};
if sign != 0 && last_sign != 0 && sign != last_sign {
variations += 1;
}
if sign != 0 {
last_sign = sign;
}
}
variations
}
pub fn stats(&self) -> &RootCountStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = RootCountStats::default();
}
}
impl Default for RootCounter {
fn default() -> Self {
Self::default_config()
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_traits::{One, Zero};
#[test]
fn test_polynomial_creation() {
let poly = Polynomial::new(vec![
BigRational::from_integer(1.into()),
BigRational::from_integer(2.into()),
BigRational::from_integer(3.into()),
]);
assert_eq!(poly.degree(), 2);
}
#[test]
fn test_polynomial_eval() {
let poly = Polynomial::new(vec![
BigRational::from_integer(1.into()),
BigRational::from_integer(2.into()),
BigRational::from_integer(3.into()),
]);
let val = poly.eval(&BigRational::zero());
assert_eq!(val, BigRational::from_integer(1.into()));
let val = poly.eval(&BigRational::one());
assert_eq!(val, BigRational::from_integer(6.into()));
}
#[test]
fn test_polynomial_derivative() {
let poly = Polynomial::new(vec![
BigRational::from_integer(1.into()),
BigRational::from_integer(2.into()),
BigRational::from_integer(3.into()),
]);
let deriv = poly.derivative();
assert_eq!(deriv.degree(), 1);
assert_eq!(deriv.coeffs[0], BigRational::from_integer(2.into()));
assert_eq!(deriv.coeffs[1], BigRational::from_integer(6.into()));
}
#[test]
fn test_descartes_rule() {
let mut counter = RootCounter::default_config();
let poly = Polynomial::new(vec![
BigRational::from_integer((-1).into()),
BigRational::zero(),
BigRational::from_integer(1.into()),
]);
let count = counter.count_positive_roots_descartes(&poly);
assert!(count >= 1); }
#[test]
fn test_counter_creation() {
let counter = RootCounter::default_config();
assert_eq!(counter.stats().sturm_sequences, 0);
}
#[test]
fn test_stats() {
let mut counter = RootCounter::default_config();
counter.stats.sturm_sequences = 5;
assert_eq!(counter.stats().sturm_sequences, 5);
counter.reset_stats();
assert_eq!(counter.stats().sturm_sequences, 0);
}
}