use pumpkin_checking::AtomicConstraint;
use pumpkin_checking::CheckerVariable;
use pumpkin_checking::InferenceChecker;
use pumpkin_checking::IntExt;
use pumpkin_checking::VariableState;
use super::shared::compute_quotient_bound_ext;
use super::shared::product_bound_ext;
#[derive(Clone, Debug)]
pub struct IntegerMultiplicationChecker<VA, VB, VC> {
pub a: VA,
pub b: VB,
pub c: VC,
}
impl<VA, VB, VC, Atomic> InferenceChecker<Atomic> for IntegerMultiplicationChecker<VA, VB, VC>
where
Atomic: AtomicConstraint,
VA: CheckerVariable<Atomic>,
VB: CheckerVariable<Atomic>,
VC: CheckerVariable<Atomic>,
{
fn check(
&self,
state: VariableState<Atomic>,
_premises: &[Atomic],
consequent: Option<&Atomic>,
) -> bool {
let a_min = self.a.induced_lower_bound(&state).into();
let a_max = self.a.induced_upper_bound(&state).into();
let b_min = self.b.induced_lower_bound(&state).into();
let b_max = self.b.induced_upper_bound(&state).into();
let c_min = self.c.induced_lower_bound(&state).into();
let c_max = self.c.induced_upper_bound(&state).into();
let check_c = || {
let (lo, hi) = product_bound_ext(a_min, a_max, b_min, b_max);
is_disjoint(lo, hi, c_min, c_max)
};
let check_a = || {
compute_quotient_bound_ext(c_min, c_max, b_min, b_max)
.is_some_and(|(lo, hi)| is_disjoint(lo, hi, a_min, a_max))
};
let check_b = || {
compute_quotient_bound_ext(c_min, c_max, a_min, a_max)
.is_some_and(|(lo, hi)| is_disjoint(lo, hi, b_min, b_max))
};
match consequent {
Some(atomic) if self.c.does_atomic_constrain_self(atomic) => check_c(),
Some(atomic) if self.a.does_atomic_constrain_self(atomic) => check_a(),
Some(atomic) if self.b.does_atomic_constrain_self(atomic) => check_b(),
Some(_) => unreachable!(),
None => check_c() || check_a() || check_b(),
}
}
}
fn is_disjoint(
lo: IntExt<i64>,
hi: IntExt<i64>,
induced_lo: IntExt<i64>,
induced_hi: IntExt<i64>,
) -> bool {
induced_hi < lo || induced_lo > hi
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn checker_detects_a_pure_conflict_with_no_consequent() {
use pumpkin_checking::Comparison;
use pumpkin_checking::TestAtomic;
use pumpkin_checking::VariableState;
let premises = [
TestAtomic {
name: "a",
comparison: Comparison::Equal,
value: 3,
},
TestAtomic {
name: "b",
comparison: Comparison::Equal,
value: 4,
},
TestAtomic {
name: "c",
comparison: Comparison::Equal,
value: 10,
},
];
let state = VariableState::prepare_for_conflict_check(premises, None)
.expect("no conflicting atomics");
let checker = IntegerMultiplicationChecker {
a: "a",
b: "b",
c: "c",
};
assert!(checker.check(state, &premises, None));
}
#[test]
fn checker_does_not_report_a_conflict_for_consistent_premises_with_no_consequent() {
use pumpkin_checking::Comparison;
use pumpkin_checking::TestAtomic;
use pumpkin_checking::VariableState;
let premises = [TestAtomic {
name: "a",
comparison: Comparison::Equal,
value: 3,
}];
let state = VariableState::prepare_for_conflict_check(premises, None)
.expect("no conflicting atomics");
let checker = IntegerMultiplicationChecker {
a: "a",
b: "b",
c: "c",
};
assert!(!checker.check(state, &premises, None));
}
}