use std::collections::HashSet;
use log::trace;
use crate::lang::{Formula, Term, Var};
use super::Environment;
pub fn remove_free_vars(formula: Formula) -> Formula {
trace!("Removing free variables from {formula}");
let mut free_vars = vec![];
let mut env = Environment::new();
find_free_vars(&formula, &mut env, &mut free_vars);
remove_duplicate(free_vars)
.iter()
.rfold(formula, |acc, free_var| {
Formula::Exists(free_var.clone(), Box::new(acc))
})
}
fn find_free_vars(formula: &Formula, env: &mut Environment, free_vars: &mut Vec<Var>) {
match formula {
Formula::Pred(pred) => pred
.get_args()
.iter()
.for_each(|arg| find_free_vars_in_terms(arg, env, free_vars)),
Formula::True => {}
Formula::False => {}
Formula::And(left, right) => {
find_free_vars(left, env, free_vars);
find_free_vars(right, env, free_vars);
}
Formula::Or(left, right) => {
find_free_vars(left, env, free_vars);
find_free_vars(right, env, free_vars);
}
Formula::Neg(subformula) => {
find_free_vars(subformula, env, free_vars);
}
Formula::Imply(left, right) => {
find_free_vars(left, env, free_vars);
find_free_vars(right, env, free_vars);
}
Formula::Iff(left, right) => {
find_free_vars(left, env, free_vars);
find_free_vars(right, env, free_vars);
}
Formula::Forall(var, subformula) => {
env.push_scope();
env.add(var.clone(), Term::Var(var.clone()));
find_free_vars(subformula, env, free_vars);
env.pop_scope();
}
Formula::Exists(var, subformula) => {
env.push_scope();
env.add(var.clone(), Term::Var(var.clone()));
find_free_vars(subformula, env, free_vars);
env.pop_scope();
}
}
}
fn find_free_vars_in_terms(term: &Term, env: &mut Environment, free_vars: &mut Vec<Var>) {
match term {
Term::Obj(_) => {}
Term::Var(var) => {
if let None = env.find(var) {
free_vars.push(var.clone());
}
}
Term::Fun(f) => f
.get_args()
.iter()
.for_each(|arg| find_free_vars_in_terms(arg, env, free_vars)),
}
}
fn remove_duplicate(vec: Vec<Var>) -> Vec<Var> {
let mut new_vec = vec![];
let mut set = HashSet::new();
for x in vec {
if !set.contains(&x) {
new_vec.push(x.clone());
}
set.insert(x);
}
new_vec
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
lang::{Pred, Term, Var},
And, Exists, Forall, Imply, Neg, Pred, Var,
};
#[test]
fn test_remove_free_vars_simple() {
let formula = Forall!(
"x",
And!(
Pred!("p", [Var!("x"), Var!("z")]),
Exists!("y", Pred!("q", [Var!("y"), Var!("w")]))
)
); let expected_result = Exists!("z", Exists!("w", formula.clone()));
assert_eq!(remove_free_vars(formula), expected_result);
}
#[test]
fn test_remove_free_vars_1() {
let formula = Imply!(
Pred!("p", [Var!("x")]),
Imply!(
Exists!(
"x",
Imply!(Pred!("q", [Var!("x")]), Pred!("r", [Var!("x"), Var!("y")]))
),
Pred!("q", [Var!("x")])
)
); let result_formula = Exists!("x", Exists!("y", formula.clone()));
assert_eq!(remove_free_vars(formula), result_formula);
}
#[test]
fn test_remove_free_vars_2() {
let formula = Forall!(
"y",
And!(
Pred!("p", [Var!["y"]]),
Neg!(Forall!(
"z",
Imply!(
Pred!("r", [Var!("z")]),
Pred!("q", [Var!("y"), Var!("z"), Var!("w")])
)
))
)
); let result_formula = Exists!("w", formula.clone());
assert_eq!(remove_free_vars(formula), result_formula);
}
}