use super::modulo_constant::{eval_u32, DIVISORS, FUZZ_INPUTS};
use super::*;
use proptest::prelude::*;
fn div_expr(divisor: u32) -> Expr {
Expr::div(Expr::var("x"), Expr::u32(divisor))
}
fn contains_add(expr: &Expr) -> bool {
match expr {
Expr::BinOp { op: BinOp::Add, .. } => true,
Expr::BinOp { left, right, .. } => contains_add(left) || contains_add(right),
Expr::UnOp { operand, .. } => contains_add(operand),
_ => false,
}
}
#[test]
fn div_reduction_exercises_both_fixup_and_nonfixup_paths() {
let three = reduce_expr(&div_expr(3)).expect("x / 3 must strength-reduce");
assert!(
!contains_add(&three),
"x / 3 is the non-fixup path (Shr(MulHigh)), must contain no Add: {three:?}"
);
let seven = reduce_expr(&div_expr(7)).expect("x / 7 must strength-reduce");
assert!(
contains_add(&seven),
"x / 7 is the fixup path, must contain the `t + ...` Add: {seven:?}"
);
}
#[test]
fn div_by_constant_is_exact_over_fuzz_and_boundaries() {
for &d in &DIVISORS {
let reduced = reduce_expr(&div_expr(d))
.unwrap_or_else(|| panic!("Fix: x / {d} must strength-reduce"));
for &x in &FUZZ_INPUTS {
assert_eq!(eval_u32(&reduced, x), x / d, "x={x} d={d}");
}
for x in [
d.wrapping_sub(1),
d,
d.wrapping_add(1),
d.wrapping_mul(2).wrapping_sub(1),
d.wrapping_mul(2),
u32::MAX - d,
u32::MAX,
] {
assert_eq!(eval_u32(&reduced, x), x / d, "boundary x={x} d={d}");
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(20_000))]
#[test]
fn div_by_constant_exact_under_proptest(x in any::<u32>(), d in 3u32..=u32::MAX) {
prop_assume!(!d.is_power_of_two());
let reduced =
reduce_expr(&div_expr(d)).expect("non-power-of-two divisor must strength-reduce");
prop_assert_eq!(eval_u32(&reduced, x), x / d);
}
}