/*
* Copyright (C) 2009-2014 University of Freiburg
*
* This file is part of SMTInterpol.
*
* SMTInterpol is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SMTInterpol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>.
*/
package de.uni_freiburg.informatik.ultimate.logic;
import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashSet;
/**
* Class to check if a term is closed. It is closed if it does not contain
* a free variable. Use it as follows:
*
* <pre>new CheckClosedTerm().isClosed()</pre>
*
* You can check multiple terms with one instance and it will cache results
* between checks.
*
* @author Jochen Hoenicke
*/
public class CheckClosedTerm extends NonRecursive {
private final ScopedHashSet<Term> mCheckedTerms;
private boolean mIsClosed;
static class TermWalker implements NonRecursive.Walker {
Term mTerm;
public TermWalker(Term term) {
mTerm = term;
}
@Override
public void walk(NonRecursive engine) {
((CheckClosedTerm) engine).check(mTerm);
}
}
static class EndScopeWalker implements NonRecursive.Walker {
@Override
public void walk(NonRecursive engine) {
((CheckClosedTerm) engine).mCheckedTerms.endScope();
}
}
/**
* The default constructor.
*/
public CheckClosedTerm() {
mCheckedTerms = new ScopedHashSet<Term>();
}
/**
* Check if term t is closed. I.e., it contains no free term variable.
* @param t the term that is checked.
* @return true if the term is closed; false otherwise.
*/
public boolean isClosed(Term t) {
mIsClosed = true;
run(new TermWalker(t));
return mIsClosed;
}
void check(Term t) {
if (mCheckedTerms.contains(t) || !mIsClosed) {
return;
}
mCheckedTerms.add(t);
if (t instanceof ApplicationTerm) {
for (final Term arg : ((ApplicationTerm)t).getParameters()) {
enqueueWalker(new TermWalker(arg));
}
} else if (t instanceof AnnotatedTerm) {
enqueueWalker(new TermWalker(((AnnotatedTerm)t).getSubterm()));
} else if (t instanceof LetTerm) {
final LetTerm let = (LetTerm) t;
for (final Term value : let.getValues()) {
enqueueWalker(new TermWalker(value));
}
mCheckedTerms.beginScope();
enqueueWalker(new EndScopeWalker());
for (final TermVariable var : let.getVariables()) {
mCheckedTerms.add(var);
}
enqueueWalker(new TermWalker(let.getSubTerm()));
} else if (t instanceof TermVariable) {
/* all bound term variables were added to mCheckedTerms */
mIsClosed = false;
} else if (t instanceof QuantifiedFormula) {
final QuantifiedFormula quant = (QuantifiedFormula) t;
mCheckedTerms.beginScope();
enqueueWalker(new EndScopeWalker());
for (final TermVariable var : quant.getVariables()) {
mCheckedTerms.add(var);
}
enqueueWalker(new TermWalker(quant.getSubformula()));
} else if (!(t instanceof ConstantTerm)) {
throw new AssertionError("Unknown term: " + t.getClass());
}
}
}