#![allow(
clippy::multiple_crate_versions,
clippy::redundant_pub_crate,
clippy::cast_possible_truncation,
clippy::missing_panics_doc,
clippy::many_single_char_names,
clippy::similar_names,
clippy::module_name_repetitions
)]
mod frac;
mod gcd;
mod linzip;
mod pgcd;
mod poly;
use num_bigint::BigInt;
use num_integer::Integer;
use num_rational::BigRational;
use num_traits::One;
use poly::IntPoly;
use polycore::Monomial;
pub use frac::Frac;
type Poly = polycore::Poly<BigRational>;
fn integral(f: &Poly) -> (IntPoly, BigInt) {
let d = f
.terms
.iter()
.fold(BigInt::one(), |d, (_, c)| d.lcm(c.denom()));
let terms = f
.terms
.iter()
.map(|(m, c)| (m.exps().to_vec(), c.numer() * (&d / c.denom())));
(IntPoly::new(f.nvars, terms), d)
}
fn rational(h: &IntPoly, d: &BigInt, like: &Poly) -> Poly {
let terms = h.terms.iter().map(|(e, c)| {
(
Monomial::new(e.clone()),
BigRational::new(c.clone(), d.clone()),
)
});
Poly::new(terms.collect(), like.nvars, like.order.clone())
}
fn normalized(h: &IntPoly, like: &Poly) -> Poly {
rational(h, &BigInt::one(), like).primitive()
}
fn gcd_int(f: &Poly, g: &Poly) -> IntPoly {
assert_eq!(f.nvars, g.nvars, "polynomials must share a ring");
gcd::gcd_z(&integral(f).0, &integral(g).0)
}
#[must_use]
pub fn gcd(f: &Poly, g: &Poly) -> Poly {
normalized(&gcd_int(f, g), f)
}
#[must_use]
pub fn cofactors(f: &Poly, g: &Poly) -> (Poly, Poly, Poly) {
let h = gcd_int(f, g);
let zero = Poly::zero(f.nvars, f.order.clone());
if h.is_zero() {
return (zero.clone(), zero.clone(), zero);
}
let h = normalized(&h, f);
let hi = integral(&h).0;
let (hf, df) = integral(f);
let (hg, dg) = integral(g);
let qf = hf.div_exact(&hi).expect("gcd divides f");
let qg = hg.div_exact(&hi).expect("gcd divides g");
(h, rational(&qf, &df, f), rational(&qg, &dg, g))
}
#[must_use]
pub fn lcm(f: &Poly, g: &Poly) -> Poly {
let h = gcd_int(f, g);
if f.is_zero() || g.is_zero() {
return Poly::zero(f.nvars, f.order.clone());
}
let (hg, _) = integral(g);
normalized(
&integral(f).0.mul(&hg.div_exact(&h).expect("gcd divides g")),
f,
)
}