use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use crate::ast::AstId;
use crate::ast::manager::AstManager;
use crate::rewriter::simplify;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Goal {
pub formulas: Vec<AstId>,
}
impl Goal {
pub fn new() -> Goal {
Goal {
formulas: Vec::new(),
}
}
pub fn from(formulas: Vec<AstId>) -> Goal {
Goal { formulas }
}
pub fn assert(&mut self, f: AstId) {
self.formulas.push(f);
}
pub fn is_decided_unsat(&self, m: &AstManager) -> bool {
self.formulas.iter().any(|&f| m.is_false(f))
}
pub fn is_decided_sat(&self, m: &AstManager) -> bool {
self.formulas.iter().all(|&f| m.is_true(f))
}
pub fn size(&self) -> usize {
self.formulas.len()
}
}
pub trait Tactic {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String>;
}
pub struct Skip;
impl Tactic for Skip {
fn apply(&self, _m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
Ok(vec![goal.clone()])
}
}
pub struct Fail;
impl Tactic for Fail {
fn apply(&self, _m: &mut AstManager, _goal: &Goal) -> Result<Vec<Goal>, String> {
Err("fail".into())
}
}
pub struct Simplify;
impl Tactic for Simplify {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
let mut out = Vec::new();
for &f in &goal.formulas {
let s = simplify(m, f);
if m.is_false(s) {
let fls = m.mk_false();
return Ok(vec![Goal::from(vec![fls])]);
}
if !m.is_true(s) {
out.push(s);
}
}
Ok(vec![Goal::from(out)])
}
}
pub struct SplitConjuncts;
impl Tactic for SplitConjuncts {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
let mut out = Vec::new();
for &f in &goal.formulas {
flatten_and(m, f, &mut out);
}
Ok(vec![Goal::from(out)])
}
}
fn flatten_and(m: &AstManager, f: AstId, out: &mut Vec<AstId>) {
if m.is_and(f) {
for &c in m.app_args(f) {
flatten_and(m, c, out);
}
} else {
out.push(f);
}
}
pub struct Then(pub Box<dyn Tactic>, pub Box<dyn Tactic>);
impl Tactic for Then {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
let mid = self.0.apply(m, goal)?;
let mut out = Vec::new();
for g in &mid {
out.extend(self.1.apply(m, g)?);
}
Ok(out)
}
}
pub struct OrElse(pub Box<dyn Tactic>, pub Box<dyn Tactic>);
impl Tactic for OrElse {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
match self.0.apply(m, goal) {
Ok(r) => Ok(r),
Err(_) => self.1.apply(m, goal),
}
}
}
pub struct Repeat(pub Box<dyn Tactic>, pub usize);
impl Tactic for Repeat {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
let mut current = vec![goal.clone()];
for _ in 0..self.1 {
let mut next = Vec::new();
for g in ¤t {
match self.0.apply(m, g) {
Ok(sub) => next.extend(sub),
Err(_) => next.push(g.clone()),
}
}
if next == current {
break;
}
current = next;
}
Ok(current)
}
}
pub struct Par(pub Box<dyn Tactic>, pub Box<dyn Tactic>);
impl Tactic for Par {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
let a = self.0.apply(m, goal);
let b = self.1.apply(m, goal);
match (a, b) {
(Ok(ra), Ok(rb)) => {
let sa: usize = ra.iter().map(Goal::size).sum();
let sb: usize = rb.iter().map(Goal::size).sum();
Ok(if sa <= sb { ra } else { rb })
}
(Ok(ra), Err(_)) => Ok(ra),
(Err(_), Ok(rb)) => Ok(rb),
(Err(e), Err(_)) => Err(e),
}
}
}
pub fn then(a: Box<dyn Tactic>, b: Box<dyn Tactic>) -> Box<dyn Tactic> {
Box::new(Then(a, b))
}
pub fn or_else(a: Box<dyn Tactic>, b: Box<dyn Tactic>) -> Box<dyn Tactic> {
Box::new(OrElse(a, b))
}
pub fn repeat(t: Box<dyn Tactic>, max: usize) -> Box<dyn Tactic> {
Box::new(Repeat(t, max))
}
pub fn par(a: Box<dyn Tactic>, b: Box<dyn Tactic>) -> Box<dyn Tactic> {
Box::new(Par(a, b))
}
pub trait Probe {
fn eval(&self, m: &AstManager, goal: &Goal) -> f64;
}
pub struct NumAssertions;
impl Probe for NumAssertions {
fn eval(&self, _m: &AstManager, goal: &Goal) -> f64 {
goal.size() as f64
}
}
pub struct NumExprs;
impl Probe for NumExprs {
fn eval(&self, m: &AstManager, goal: &Goal) -> f64 {
goal.formulas
.iter()
.map(|&f| m.num_subexprs(f))
.sum::<usize>() as f64
}
}
pub struct Cond {
pub probe: Box<dyn Probe>,
pub threshold: f64,
pub then_t: Box<dyn Tactic>,
pub else_t: Box<dyn Tactic>,
}
impl Tactic for Cond {
fn apply(&self, m: &mut AstManager, goal: &Goal) -> Result<Vec<Goal>, String> {
if self.probe.eval(m, goal) >= self.threshold {
self.then_t.apply(m, goal)
} else {
self.else_t.apply(m, goal)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simplify_tactic_folds_and_detects_unsat() {
let mut m = AstManager::new();
let x = m.mk_int_const("x");
let three = m.mk_int(3);
let lt = m.mk_lt(x, three);
let tru = m.mk_true();
let goal = Goal::from(vec![lt, tru]);
let out = Simplify.apply(&mut m, &goal).unwrap();
assert_eq!(out.len(), 1);
assert_eq!(out[0].formulas, vec![lt]);
let one = m.mk_int(1);
let two = m.mk_int(2);
let eq = m.mk_eq(one, two); let g2 = Goal::from(vec![eq]);
let out2 = Simplify.apply(&mut m, &g2).unwrap();
assert!(out2[0].is_decided_unsat(&m));
}
#[test]
fn split_conjuncts_flattens() {
let mut m = AstManager::new();
let p = m.mk_bool_const("p");
let q = m.mk_bool_const("q");
let r = m.mk_bool_const("r");
let inner = m.mk_and(&[q, r]);
let outer = m.mk_and(&[p, inner]);
let goal = Goal::from(vec![outer]);
let out = SplitConjuncts.apply(&mut m, &goal).unwrap();
assert_eq!(out[0].formulas, vec![p, q, r]);
}
#[test]
fn combinators_compose() {
let mut m = AstManager::new();
let p = m.mk_bool_const("p");
let tru = m.mk_true();
let inner = m.mk_and(&[p, tru]);
let goal = Goal::from(vec![inner]);
let t = then(Box::new(SplitConjuncts), Box::new(Simplify));
let out = t.apply(&mut m, &goal).unwrap();
assert_eq!(out[0].formulas, vec![p]);
let oe = or_else(Box::new(Fail), Box::new(Skip));
assert!(oe.apply(&mut m, &goal).is_ok());
let rp = repeat(Box::new(Simplify), 100);
assert!(rp.apply(&mut m, &goal).is_ok());
}
#[test]
fn probe_guarded_cond() {
let mut m = AstManager::new();
let p = m.mk_bool_const("p");
let q = m.mk_bool_const("q");
let goal = Goal::from(vec![p, q]);
assert_eq!(NumAssertions.eval(&m, &goal), 2.0);
let cond = Cond {
probe: Box::new(NumAssertions),
threshold: 2.0,
then_t: Box::new(Fail),
else_t: Box::new(Skip),
};
assert!(cond.apply(&mut m, &goal).is_err());
let small = Goal::from(vec![p]);
assert!(cond.apply(&mut m, &small).is_ok());
}
}