use ocas_domain::EuclideanDomain;
use ocas_domain::{FiniteField, IntegerDomain};
use crate::dense::DenseUnivariatePolynomial;
pub mod finite_field;
pub mod hensel;
pub mod multivariate;
pub type SquareFreeFactors<D> = Vec<(DenseUnivariatePolynomial<D>, usize)>;
pub type Factors<D> = Vec<(DenseUnivariatePolynomial<D>, usize)>;
impl<D: EuclideanDomain> DenseUnivariatePolynomial<D> {
pub fn square_free_factorization(&self) -> SquareFreeFactors<D> {
let mut factors = SquareFreeFactors::new();
if self.is_zero() {
return factors;
}
let f = self.primitive_part();
let f_deriv = f.derivative();
let mut g = f.gcd(&f_deriv);
if g.is_zero() {
return factors;
}
let mut w = match f.div_rem(&g) {
Some((q, _)) => q,
None => return factors,
};
let mut k = 1;
while !w.is_one() {
let h = w.gcd(&g);
if let Some((z, _)) = w.div_rem(&h)
&& !z.is_one()
&& !z.is_zero()
{
factors.push((z, k));
}
if let Some((q, _)) = g.div_rem(&h) {
g = q;
} else {
break;
}
w = h;
k += 1;
}
factors
}
pub fn is_square_free(&self) -> bool {
if self.degree().unwrap_or(0) <= 1 {
return true;
}
let deriv = self.derivative();
let g = self.gcd(&deriv);
g.degree() == Some(0)
}
}
impl DenseUnivariatePolynomial<IntegerDomain> {
pub fn factor(&self) -> Factors<IntegerDomain> {
hensel::factor_primitive(self)
}
}
impl DenseUnivariatePolynomial<FiniteField> {
pub fn factor(&self) -> Factors<FiniteField> {
finite_field::factor_over_finite_field(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ocas_domain::{Integer, IntegerDomain};
fn i(n: i64) -> Integer {
Integer::from(n)
}
#[test]
fn square_free_x_plus_1_cubed() {
let d = IntegerDomain;
let p = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(3), i(3), i(1)]);
let factors = p.square_free_factorization();
assert!(!factors.is_empty());
for (factor, mult) in &factors {
if factor.degree() == Some(1) {
assert_eq!(*mult, 3);
}
}
}
#[test]
fn is_square_free_linear() {
let d = IntegerDomain;
let p = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(1)]); assert!(p.is_square_free());
}
#[test]
fn is_not_square_free_perfect_square() {
let d = IntegerDomain;
let p = DenseUnivariatePolynomial::from_coeffs(d, vec![i(1), i(2), i(1)]); assert!(!p.is_square_free());
}
#[test]
fn square_free_x2_minus_1() {
let d = IntegerDomain;
let p = DenseUnivariatePolynomial::from_coeffs(d, vec![i(-1), i(0), i(1)]); let factors = p.square_free_factorization();
assert!(p.is_square_free());
assert!(!factors.is_empty());
}
}