extern crate groebner;
use groebner::{groebner_basis, MonomialOrder, Polynomial, PolynomialRing};
use num_rational::BigRational;
use std::time::Instant;
const SEED: usize = 7919;
const TEST_VARIABLES: [&str; 5] = ["x", "y", "z", "w", "u"];
#[allow(clippy::expect_used)]
fn make_poly(
nvars: usize,
max_deg: u32,
nterms: usize,
order: MonomialOrder,
offset: usize,
) -> Polynomial<BigRational> {
let ring = PolynomialRing::<BigRational>::new(TEST_VARIABLES[..nvars].iter().copied(), order)
.expect("test ring should be valid");
let mut expression = String::new();
for i in 0..nterms {
let mut exps = vec![0u32; nvars];
for (v, exp) in exps.iter_mut().enumerate() {
*exp = (((i + v * offset + SEED * v) * (v + 1)) % (max_deg as usize + 1)) as u32;
}
let sign = if (i + offset) % 2 == 0 { 1 } else { -1 };
push_term(
&mut expression,
i,
sign * ((i + 1 + offset) as i32),
1 + (i % 3) as i32,
&exps,
);
}
ring.parse(&expression)
.expect("generated stress polynomial should parse")
}
fn push_term(
expression: &mut String,
index: usize,
numerator: i32,
denominator: i32,
exponents: &[u32],
) {
let negative = numerator < 0;
let abs_numerator = numerator.abs();
if index == 0 {
if negative {
expression.push('-');
}
} else if negative {
expression.push_str(" - ");
} else {
expression.push_str(" + ");
}
let is_constant = exponents.iter().all(|&exponent| exponent == 0);
let mut factors = Vec::new();
if abs_numerator != 1 || denominator != 1 || is_constant {
if denominator == 1 {
factors.push(abs_numerator.to_string());
} else {
factors.push(format!("{abs_numerator}/{denominator}"));
}
}
for (variable, exponent) in TEST_VARIABLES.iter().zip(exponents) {
match exponent {
0 => {}
1 => factors.push((*variable).to_string()),
exponent => factors.push(format!("{variable}^{exponent}")),
}
}
expression.push_str(&factors.join("*"));
}
#[test]
fn stress_small() {
let nvars = 2;
let nterms = 3;
let polys: Vec<_> = (0..2)
.map(|i| make_poly(nvars, 2, nterms, MonomialOrder::Lex, i))
.collect();
let start = Instant::now();
let basis = groebner_basis(polys, MonomialOrder::Lex, true).expect("Groebner failed");
let elapsed = start.elapsed();
println!(
"[stress_small] Time: {}.{:03} seconds, basis size: {}",
elapsed.as_secs(),
elapsed.subsec_millis(),
basis.len()
);
}
#[test]
fn stress_medium() {
let nvars = 3;
let nterms = 4;
let polys: Vec<_> = (0..3)
.map(|i| make_poly(nvars, 3, nterms, MonomialOrder::GrLex, i))
.collect();
let start = Instant::now();
let basis = groebner_basis(polys, MonomialOrder::GrLex, true).expect("Groebner failed");
let elapsed = start.elapsed();
println!(
"[stress_medium] Time: {}.{:03} seconds, basis size: {}",
elapsed.as_secs(),
elapsed.subsec_millis(),
basis.len()
);
}
#[test]
fn stress_large() {
let nvars = 4;
let nterms = 5;
let polys: Vec<_> = (0..4)
.map(|i| make_poly(nvars, 3, nterms, MonomialOrder::Lex, i))
.collect();
let start = Instant::now();
let basis = groebner_basis(polys, MonomialOrder::Lex, true).expect("Groebner failed");
let elapsed = start.elapsed();
println!(
"[stress_large] Time: {}.{:03} seconds, basis size: {}",
elapsed.as_secs(),
elapsed.subsec_millis(),
basis.len()
);
}
#[test]
fn stress_xlarge() {
let nvars = 5;
let nterms = 8;
let polys: Vec<_> = (0..5)
.map(|i| make_poly(nvars, 5, nterms, MonomialOrder::GrLex, i))
.collect();
let start = Instant::now();
let basis = groebner_basis(polys, MonomialOrder::GrLex, true).expect("Groebner failed");
let elapsed = start.elapsed();
println!(
"[stress_xlarge] Time: {}.{:03} seconds, basis size: {}",
elapsed.as_secs(),
elapsed.subsec_millis(),
basis.len()
);
}