use num_bigint::BigInt;
use num_rational::BigRational;
use oxiz_math::grobner::buchberger::PolynomialConstraint;
use oxiz_math::grobner::{NraSolver, SatResult, reduce};
use oxiz_math::polynomial::Polynomial;
fn rat(n: i64) -> BigRational {
BigRational::from_integer(BigInt::from(n))
}
#[test]
fn sturm_count_roots_negative_leading_coeff() {
let p = Polynomial::from_coeffs_int(&[(-1, &[(0, 2)]), (1, &[])]);
let count = p.count_roots_in_interval(0, &rat(-2), &rat(2));
assert_eq!(count, 2, "-x^2+1 must have 2 real roots in (-2,2)");
}
#[test]
fn sturm_count_roots_sign_agnostic() {
let p_neg = Polynomial::from_coeffs_int(&[(-1, &[(0, 2)]), (1, &[])]);
let p_pos = Polynomial::from_coeffs_int(&[(1, &[(0, 2)]), (-1, &[])]);
let c_neg = p_neg.count_roots_in_interval(0, &rat(-2), &rat(2));
let c_pos = p_pos.count_roots_in_interval(0, &rat(-2), &rat(2));
assert_eq!(c_neg, 2);
assert_eq!(c_pos, 2);
assert_eq!(
c_neg, c_pos,
"root count must be independent of overall sign"
);
}
#[test]
fn sturm_count_roots_single_root_interval() {
let p = Polynomial::from_coeffs_int(&[(-1, &[(0, 2)]), (1, &[])]);
let count = p.count_roots_in_interval(0, &rat(0), &rat(2));
assert_eq!(count, 1);
}
#[test]
fn nra_linear_inequalities_not_wrongly_sat() {
let mut solver = NraSolver::new();
let x = Polynomial::from_var(0);
solver.add_constraint(PolynomialConstraint::greater(x.clone())); solver.add_constraint(PolynomialConstraint::less(x));
let result = solver.check_sat();
assert_ne!(
result,
SatResult::Sat,
"unsatisfiable linear inequalities must never be reported Sat"
);
assert_eq!(
result,
SatResult::Unsat,
"x > 0 and x < 0 is genuinely unsatisfiable and the solver now has a \
linear decision procedure (Fourier-Motzkin) that proves it, so the \
correct verdict is Unsat, not merely Unknown"
);
}
#[test]
fn nra_single_linear_inequality_is_decided_sat() {
let mut solver = NraSolver::new();
let x = Polynomial::from_var(0);
solver.add_constraint(PolynomialConstraint::greater(x)); assert_eq!(
solver.check_sat(),
SatResult::Sat,
"x > 0 is satisfiable (witness x = 1); the linear decision \
procedure must decide it, not fall back to Unknown"
);
}
#[test]
fn nra_nonlinear_inequality_still_honestly_unknown() {
let mut solver = NraSolver::new();
let x_squared = Polynomial::from_coeffs_int(&[(1, &[(0, 2)])]);
solver.add_constraint(PolynomialConstraint::greater(x_squared)); assert_eq!(
solver.check_sat(),
SatResult::Unknown,
"non-linear constraints have no decision procedure wired in; the \
solver must say Unknown rather than guess"
);
}
#[test]
fn nra_constant_inequalities_still_decided() {
let mut solver = NraSolver::new();
solver.add_constraint(PolynomialConstraint::greater(Polynomial::constant(rat(-1))));
assert_eq!(solver.check_sat(), SatResult::Unsat);
let mut solver2 = NraSolver::new();
solver2.add_constraint(PolynomialConstraint::greater(Polynomial::constant(rat(1))));
assert_eq!(solver2.check_sat(), SatResult::Sat);
}
#[test]
fn reduce_does_not_drop_tail_at_iteration_cap() {
let mut f = Polynomial::zero();
for k in 0..1200u32 {
f = f.add(&Polynomial::from_coeffs_int(&[(1, &[(0, k)])]));
}
let g = Polynomial::from_var(1);
let reduced = reduce(&f, &[g]);
let diff = reduced.sub(&f);
assert!(
diff.is_zero(),
"reduce() dropped part of the polynomial at the iteration cap"
);
}