use crate::math::gcd;
use crate::math::poly::Poly;
use crate::poly;
use core::cmp::{max, min};
pub fn gcd_poly_zz_heu(f: Poly, g: Poly) -> Result<(Poly, Poly, Poly), &'static str> {
if let Some(res) = trivial_gcd(&f, &g) {
return Ok(res);
}
let df = f.deg();
let dg = g.deg();
let (gcd_val, f, g) = poly_extract_common(f, g);
if df == 0 || dg == 0 {
return Ok((poly![gcd_val], f, g));
}
let f_norm = f.max_norm();
let g_norm = g.max_norm();
let norm_char = 2 * min(f_norm, g_norm) + 29;
let mut xi: isize = max(
min(norm_char, 10_000 * (norm_char as f64).sqrt() as usize / 101),
2 * min(
f_norm / f.lc().abs() as usize,
g_norm / g.lc().abs() as usize + 2,
),
) as isize;
const GCD_HEU_MAX_ITER: u8 = 6;
let mut f_xi;
let mut g_xi;
let mut heu;
let mut heu_poly;
let mut cff;
let mut cfg;
let mut cff_poly;
let mut cfg_poly;
let mut _cff2;
let mut _cfg2;
let mut r;
for _ in 0..GCD_HEU_MAX_ITER {
f_xi = f.eval(xi as isize);
g_xi = g.eval(xi as isize);
if f_xi != 0 && g_xi != 0 {
heu = gcd(f_xi.abs() as usize, g_xi.abs() as usize) as isize;
cff = f_xi / heu;
cfg = g_xi / heu;
heu_poly = gcd_interpolate(heu, xi);
heu_poly = heu_poly.primitive();
match f.clone().div(heu_poly.clone()) {
Ok(q) => {
_cff2 = q.0;
r = q.1
}
Err(e) => return Err(e),
}
if r.is_zero() {
match g.clone().div(heu_poly.clone()) {
Ok(q) => {
_cfg2 = q.0;
r = q.1;
}
Err(e) => return Err(e),
}
if r.is_zero() {
heu_poly = heu_poly.mul_scalar(gcd_val);
return Ok((heu_poly, _cff2, _cfg2));
}
}
cff_poly = gcd_interpolate(cff, xi);
match f.clone().div(cff_poly.clone()) {
Ok(q) => {
heu_poly = q.0;
r = q.1;
}
Err(e) => return Err(e),
}
if r.is_zero() {
match g.clone().div(heu_poly.clone()) {
Ok(q) => {
_cfg2 = q.0;
r = q.1;
}
Err(e) => return Err(e),
}
if r.is_zero() {
heu_poly = heu_poly.mul_scalar(gcd_val);
return Ok((heu_poly, cff_poly, _cfg2));
}
}
cfg_poly = gcd_interpolate(cfg, xi);
match g.clone().div(cfg_poly.clone()) {
Ok(q) => {
heu_poly = q.0;
r = q.1;
}
Err(e) => return Err(e),
}
if r.is_zero() {
match f.clone().div(heu_poly.clone()) {
Ok(q) => {
_cff2 = q.0;
r = q.1;
}
Err(e) => return Err(e),
}
if r.is_zero() {
heu_poly = heu_poly.mul_scalar(gcd_val);
return Ok((heu_poly, _cff2, cfg_poly));
}
}
}
xi = 73794 * xi * ((xi as f64).sqrt().sqrt() as isize)
}
Err("gcd_poly_zz_heu failed")
}
#[cfg(feature = "benchmark-internals")]
pub fn _gcd_poly_zz_heu<T, U>(f: T, g: U) -> Result<(Poly, Poly, Poly), &'static str>
where
T: Into<Poly>,
U: Into<Poly>,
{
gcd_poly_zz_heu(f.into(), g.into())
}
fn poly_coeffs_gcd(p: &Poly) -> usize {
if p.is_zero() {
return 0;
}
let mut cont: usize = p.vec[0] as usize;
for i in &p.vec {
cont = gcd(cont, i.abs() as usize);
if cont == 1 {
break;
}
}
cont as usize
}
fn poly_extract_common(mut f: Poly, mut g: Poly) -> (isize, Poly, Poly) {
let fc = poly_coeffs_gcd(&f);
let gc = poly_coeffs_gcd(&g);
let gcd = gcd(fc, gc) as isize;
if gcd == 0 {
(1, f, g)
} else {
f = f.div_scalar(gcd).unwrap();
g = g.div_scalar(gcd).unwrap();
(gcd, f, g)
}
}
fn trivial_gcd(f: &Poly, g: &Poly) -> Option<(Poly, Poly, Poly)> {
match (f.is_zero(), g.is_zero()) {
(true, true) => Some((poly![], poly![], poly![])),
(false, true) => Some((f.clone(), poly![], poly![])),
(true, false) => Some((g.clone(), poly![], poly![])),
(false, false) => None,
}
}
fn gcd_interpolate(mut h: isize, x: isize) -> Poly {
let mut res = Vec::new();
let mut g: isize;
while h != 0 {
g = h % x;
if g > x / 2 {
g -= x
}
res.push(g);
h = (h - g) / x;
}
Poly::new(res.into_iter().rev().collect())
}
#[cfg(test)]
mod test {
use super::gcd_poly_zz_heu;
use crate::math::poly::Poly;
use crate::poly;
#[test]
fn test_gcd_poly_zz_heu_1() {
assert_eq!(
gcd_poly_zz_heu(poly![1, 0, -1], poly![1, -3, 2]),
Ok((poly![1, -1], poly![1, 1], poly![1, -2]))
)
}
#[test]
fn test_gcd_poly_zz_heu_2() {
assert_eq!(
gcd_poly_zz_heu(poly![1, 10, 35, 50, 24], poly![1, 1]),
Ok((poly![1, 1], poly![1, 9, 26, 24], poly![1]))
)
}
#[test]
fn test_gcd_poly_zz_heu_3() {
assert_eq!(
gcd_poly_zz_heu(poly![1, -9, -1, 9], poly![1, -8, -9]),
Ok((poly![1, -8, -9], poly![1, -1], poly![1]))
)
}
}