/*
* Copyright (C) 2009-2012 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 java.util.LinkedHashSet;
import de.uni_freiburg.informatik.ultimate.logic.Script.LBool;
/**
* This class contains some static methods to help creating terms, checking
* formulas, etc.
*
* @author christ, heizmann, hoenicke
*/
public final class Util {
private Util() {
// Prevent instantiation of this utility class
}
/**
* Check if {@code term} which may contain free {@code TermVariables} is
* satisfiable with respect to the current assertion stack of
* {@code script}. Only the result from this function can be used since the
* assertion stack will be modified after leaving this function.
* @param script the script used to run the check.
* @param term the term to check for satisfiability (possibly containing
* free variables).
* @return the satisfiability status (SAT, UNSAT or UNKNOWN).
*/
public static LBool checkSat(Script script, Term term) {
script.push(1);
try {
final TermVariable[] vars = term.getFreeVars();
final Term[] values = new Term[vars.length];
for (int i = 0; i < vars.length; i++) {
values[i] = termVariable2constant(script, vars[i]);
}
term = script.let(vars, values, term);
LBool result = script.assertTerm(term);
if (result == LBool.UNKNOWN) {
result = script.checkSat();
}
return result;
} finally {
script.pop(1);
}
}
private static Term termVariable2constant(Script script, TermVariable tv) {
final String name = tv.getName() + "_const_" + tv.hashCode();
final Sort resultSort = tv.getSort();
script.declareFun(name, Script.EMPTY_SORT_ARRAY, resultSort);
return script.term(name);
}
/**
* Return slightly simplified version of (not f). It removes
* double negation and simplifies (not true) and (not false).
* @param script the Script used to build terms.
* @param f the term to negate
* @return a term logically equivalent to (not f).
*/
public static Term not(Script script, Term f) {
if (f == script.term("true")) {
return script.term("false");
}
if (f == script.term("false")) {
return script.term("true");
}
if (f instanceof ApplicationTerm
&& ((ApplicationTerm)f).getFunction().getName().equals("not")) {
return ((ApplicationTerm) f).getParameters()[0];
}
return script.term("not", f);
}
/**
* Return slightly simplified version of (and subforms). It removes
* parameters occuring multiple times, true and false. It also handles
* the case where there is only one or zero subformulas.
* @param script the Script used to build terms.
* @param subforms the sub formulas that are conjoined.
* @return a term logically equivalent to (and subforms).
*/
public static Term and(Script script, Term... subforms) {
return simplifyAndOr(script, "and", subforms);
}
/**
* Return slightly simplified version of (or subforms). It removes
* parameters occuring multiple times, true and false. It also handles
* the case where there is only one or zero subformulas.
* @param script the Script used to build terms.
* @param subforms the sub formulas that are disjoined.
* @return a term logically equivalent to (or subforms).
*/
public static Term or(Script script, Term... subforms) {
return simplifyAndOr(script, "or", subforms);
}
private static Term simplifyAndOr(
Script script, String connector, Term... subforms) {
final Term trueTerm = script.term("true");
final Term falseTerm = script.term("false");
Term neutral, absorbing;
if (connector.equals("and")) {
neutral = trueTerm;
absorbing = falseTerm;
} else {
neutral = falseTerm;
absorbing = trueTerm;
}
final LinkedHashSet<Term> formulas = new LinkedHashSet<Term>();
for (final Term f : subforms) {
if (f == neutral) {
continue;
}
if (f == absorbing) {
return f;
}
/* Normalize nested and/ors */
if (f instanceof ApplicationTerm
&& ((ApplicationTerm) f).getFunction().getName().equals(
connector)) {
for (final Term subf : ((ApplicationTerm) f).getParameters()) {
formulas.add(subf);
}
} else {
formulas.add(f);
}
}
if (formulas.size() <= 1) { //NOPMD
if (formulas.isEmpty()) {
return neutral;
} else {
return formulas.iterator().next();
}
}
final Term[] arrforms = formulas.toArray(new Term[formulas.size()]);
return script.term(connector, arrforms);
}
/**
* Create a slightly simplified if-then-else term. This mainly
* optimizes the special cases where one of the parameters is true or
* false.
* @param script the script where the term is created.
* @param cond the if condition.
* @param thenPart the then part.
* @param elsePart the else part.
* @return the simplified if-then-else term.
*/
public static Term ite(
Script script, Term cond, Term thenPart, Term elsePart) {
final Term trueTerm = script.term("true");
final Term falseTerm = script.term("false");
if (cond == trueTerm || thenPart == elsePart) {
return thenPart;
} else if (cond == falseTerm) {
return elsePart;
} else if (thenPart == trueTerm) {
return Util.or(script, cond, elsePart);
} else if (elsePart == falseTerm) {
return Util.and(script, cond, thenPart);
} else if (thenPart == falseTerm) {
return Util.and(script, Util.not(script, cond), elsePart);
} else if (elsePart == trueTerm) {
return Util.or(script, Util.not(script, cond), thenPart);
}
return script.term("ite", cond, thenPart, elsePart);
}
/**
* Create a slightly simplified implies term. This mainly
* optimizes the special cases where one of the parameters is true or
* false or if a left-hand-side term occurs more than once.
* It also handles the case where only one subformula is given.
* @param script the script where the term is created.
* @param subforms the sub formulas.
* @return A simplified version of <code>(=> subforms...)</code>.
*/
public static Term implies(Script script, Term... subforms) {
final Term trueTerm = script.term("true");
final Term lastFormula = subforms[subforms.length - 1];
if (lastFormula == trueTerm) {
return trueTerm;
}
final Term falseTerm = script.term("false");
if (lastFormula == falseTerm) {
final Term[] allButLast = new Term[subforms.length - 1];
System.arraycopy(subforms, 0, allButLast, 0, subforms.length - 1);
return Util.not(script, Util.and(script, allButLast));
}
final LinkedHashSet<Term> newSubforms = new LinkedHashSet<Term>();
for (int i = 0; i < subforms.length - 1; i++) {
if (subforms[i] == falseTerm) {
return trueTerm;
}
if (subforms[i] != trueTerm) {
newSubforms.add(subforms[i]);
}
}
if (newSubforms.isEmpty()) {
return lastFormula;
}
final Term[] newParams = newSubforms.toArray(
new Term[newSubforms.size() + 1]);
newParams[newParams.length - 1] = lastFormula;
return script.term("=>", newParams);
}
}