use super::Polynomial;
#[allow(unused_imports)]
use crate::prelude::*;
use num_rational::BigRational;
use num_traits::{One, Zero};
#[derive(Debug, Clone)]
pub struct GcdConfig {
pub use_modular: bool,
pub use_subresultant: bool,
pub modulus: u64,
pub max_degree: u32,
}
impl Default for GcdConfig {
fn default() -> Self {
Self {
use_modular: true,
use_subresultant: true,
modulus: 2147483647, max_degree: 1000,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct GcdStats {
pub gcds_computed: u64,
pub euclidean_steps: u64,
pub modular_reductions: u64,
pub time_us: u64,
}
pub struct PolynomialGcd {
config: GcdConfig,
stats: GcdStats,
}
impl PolynomialGcd {
pub fn new() -> Self {
Self::with_config(GcdConfig::default())
}
pub fn with_config(config: GcdConfig) -> Self {
Self {
config,
stats: GcdStats::default(),
}
}
pub fn stats(&self) -> &GcdStats {
&self.stats
}
pub fn gcd(&mut self, a: &Polynomial, b: &Polynomial) -> Polynomial {
#[cfg(feature = "std")]
let start = std::time::Instant::now();
if a.is_zero() {
self.stats.gcds_computed += 1;
return b.clone();
}
if b.is_zero() {
self.stats.gcds_computed += 1;
return a.clone();
}
if a.total_degree() > self.config.max_degree || b.total_degree() > self.config.max_degree {
self.stats.gcds_computed += 1;
return Polynomial::constant(BigRational::one());
}
let result = if a.total_degree() < 10 && b.total_degree() < 10 {
self.euclidean_gcd(a, b)
} else if self.config.use_modular {
self.modular_gcd(a, b)
} else {
self.euclidean_gcd(a, b)
};
self.stats.gcds_computed += 1;
#[cfg(feature = "std")]
{
self.stats.time_us += start.elapsed().as_micros() as u64;
}
result
}
fn euclidean_gcd(&mut self, a: &Polynomial, b: &Polynomial) -> Polynomial {
let mut r0 = a.clone();
let mut r1 = b.clone();
while !r1.is_zero() {
self.stats.euclidean_steps += 1;
let remainder = self.polynomial_remainder(&r0, &r1);
r0 = r1;
r1 = remainder;
}
self.normalize_polynomial(r0)
}
fn modular_gcd(&mut self, a: &Polynomial, b: &Polynomial) -> Polynomial {
self.stats.modular_reductions += 1;
self.euclidean_gcd(a, b)
}
fn polynomial_remainder(&self, a: &Polynomial, b: &Polynomial) -> Polynomial {
if b.is_zero() {
panic!("Division by zero polynomial");
}
if a.total_degree() < b.total_degree() {
return a.clone();
}
Polynomial::zero()
}
fn normalize_polynomial(&self, mut poly: Polynomial) -> Polynomial {
if poly.is_zero() {
return poly;
}
if let Some(leading) = poly.terms.first() {
let leading_coeff = leading.coeff.clone();
if !leading_coeff.is_one() && !leading_coeff.is_zero() {
for _term in &mut poly.terms {
}
}
}
poly
}
pub fn gcd_multiple(&mut self, polys: &[Polynomial]) -> Polynomial {
if polys.is_empty() {
return Polynomial::zero();
}
let mut result = polys[0].clone();
for poly in &polys[1..] {
result = self.gcd(&result, poly);
if result.total_degree() == 0 && !result.is_zero() {
break;
}
}
result
}
pub fn lcm(&mut self, a: &Polynomial, b: &Polynomial) -> Polynomial {
if a.is_zero() || b.is_zero() {
return Polynomial::zero();
}
let gcd = self.gcd(a, b);
if gcd.is_zero() {
return Polynomial::zero();
}
a.mul(b)
}
}
impl Default for PolynomialGcd {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
use num_rational::BigRational;
#[test]
fn test_gcd_creation() {
let gcd_engine = PolynomialGcd::new();
assert_eq!(gcd_engine.stats().gcds_computed, 0);
}
#[test]
fn test_gcd_zero() {
let mut gcd_engine = PolynomialGcd::new();
let a = Polynomial::constant(BigRational::from(BigInt::from(42)));
let b = Polynomial::zero();
let result = gcd_engine.gcd(&a, &b);
assert!(!result.is_zero());
assert_eq!(gcd_engine.stats().gcds_computed, 1);
}
#[test]
fn test_gcd_constants() {
let mut gcd_engine = PolynomialGcd::new();
let a = Polynomial::constant(BigRational::from(BigInt::from(12)));
let b = Polynomial::constant(BigRational::from(BigInt::from(18)));
let result = gcd_engine.gcd(&a, &b);
assert!(!result.is_zero());
}
#[test]
fn test_gcd_config() {
let config = GcdConfig {
use_modular: false,
use_subresultant: false,
..Default::default()
};
let gcd_engine = PolynomialGcd::with_config(config);
assert!(!gcd_engine.config.use_modular);
}
#[test]
fn test_gcd_multiple() {
let mut gcd_engine = PolynomialGcd::new();
let polys = vec![
Polynomial::constant(BigRational::from(BigInt::from(12))),
Polynomial::constant(BigRational::from(BigInt::from(18))),
Polynomial::constant(BigRational::from(BigInt::from(24))),
];
let result = gcd_engine.gcd_multiple(&polys);
assert!(!result.is_zero());
}
#[test]
fn test_lcm() {
let mut gcd_engine = PolynomialGcd::new();
let a = Polynomial::constant(BigRational::from(BigInt::from(4)));
let b = Polynomial::constant(BigRational::from(BigInt::from(6)));
let result = gcd_engine.lcm(&a, &b);
assert!(!result.is_zero());
}
}