zippel-gcd 0.5.0

Sparse multivariate polynomial GCD, cofactors and LCM over the rationals.
Documentation
use polycore::{Order, Ring};
use zippel_gcd::{
    gcd, gcd_with_algorithm, gcd_with_report, hu_monagan, hu_monagan_bivariate, huang_gao,
    huang_monagan, zippel, GcdAlgorithm,
};

#[test]
fn named_algorithms_recover_the_same_normalized_gcd() {
    let ring = Ring::new(["x", "y", "z"], Order::GRevLex);
    let h = ring.parse("(2*x+y+1)^2").unwrap();
    let a = &h * &ring.parse("-2/7*x^2*y*(x+z+2)").unwrap();
    let b = &h * &ring.parse("3/11*x*y^2*(y+z+3)").unwrap();
    let expected = (&h * &ring.parse("x*y").unwrap()).primitive();
    assert_eq!(gcd(&a, &b), expected);
    let (value, report) = gcd_with_report(&a, &b, GcdAlgorithm::HuangGao);
    assert_eq!(value, expected);
    assert_eq!(report.attempted, vec![GcdAlgorithm::HuangGao]);
    assert_eq!(zippel(&a, &b), expected);
    for algorithm in [huang_gao, huang_monagan, hu_monagan, hu_monagan_bivariate] {
        assert_eq!(algorithm(&a, &b).unwrap(), expected);
        assert_eq!(algorithm(&b, &a).unwrap(), expected);
    }
}

#[test]
fn direct_recovery_declines_while_aggregate_apis_fall_back() {
    let ring = Ring::new(["x"], Order::Lex);
    let f = ring.parse("x^65537+1").unwrap();
    for (direct, selected) in [
        (huang_gao as fn(_, _) -> _, GcdAlgorithm::HuangGao),
        (huang_monagan, GcdAlgorithm::HuangMonagan),
        (hu_monagan, GcdAlgorithm::HuMonagan),
        (hu_monagan_bivariate, GcdAlgorithm::HuMonaganBivariate),
    ] {
        assert!(direct(&f, &f).is_none(), "{selected:?} must not fall back");
        assert_eq!(gcd_with_algorithm(&f, &f, selected), f);
        let (value, report) = gcd_with_report(&f, &f, selected);
        assert_eq!(value, f);
        let expected = if selected == GcdAlgorithm::HuangGao {
            vec![selected, GcdAlgorithm::HuangMonagan, GcdAlgorithm::Zippel]
        } else {
            vec![selected, GcdAlgorithm::Zippel]
        };
        assert_eq!(report.attempted, expected);
    }
    assert_eq!(gcd(&f, &f), f);
    assert_eq!(zippel(&f, &f), f);
    assert_eq!(
        gcd_with_report(&f, &f, GcdAlgorithm::Zippel).1.attempted,
        vec![GcdAlgorithm::Zippel]
    );
}

#[test]
fn named_algorithms_preserve_zero_and_constant_conventions() {
    let ring = Ring::new(["x", "y"], Order::Lex);
    let zero = ring.parse("0").unwrap();
    let f = ring.parse("-6*x+12*y").unwrap();
    let constant = ring.parse("15").unwrap();
    for algorithm in [huang_gao, huang_monagan, hu_monagan, hu_monagan_bivariate] {
        assert_eq!(algorithm(&zero, &zero), Some(zero.clone()));
        assert_eq!(algorithm(&zero, &f), Some(f.primitive()));
        assert_eq!(algorithm(&f, &zero), Some(f.primitive()));
        assert_eq!(algorithm(&constant, &f), Some(ring.parse("1").unwrap()));
    }
    assert_eq!(zippel(&zero, &zero), zero);
    assert_eq!(zippel(&zero, &f), f.primitive());
    assert_eq!(zippel(&f, &zero), f.primitive());
    assert_eq!(zippel(&constant, &f), ring.parse("1").unwrap());
}