/* * Kodkod -- Copyright (c) 2005-present, Emina Torlak * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package kodkod.ast; import static kodkod.ast.operator.FormulaOperator.AND; import static kodkod.ast.operator.FormulaOperator.IFF; import static kodkod.ast.operator.FormulaOperator.IMPLIES; import static kodkod.ast.operator.FormulaOperator.OR; import static kodkod.ast.operator.Quantifier.ALL; import static kodkod.ast.operator.Quantifier.SOME; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import kodkod.ast.operator.FormulaOperator; import kodkod.ast.operator.Quantifier; import kodkod.ast.visitor.ReturnVisitor; import kodkod.util.collections.Containers; /** * A first-order formula. Unless otherwise noted, * all methods in this class throw a NullPointerException when given * null arguments. * @author Emina Torlak */ public abstract class Formula extends Node { /** Constant formula true */ public static final Formula TRUE = new ConstantFormula(true) {}; /** Constant formula false */ public static final Formula FALSE = new ConstantFormula(false) {}; Formula() {} /** * Returns the constant formula with the given value. * @return value ? TRUE : FALSE */ public static Formula constant(boolean value) { return value ? TRUE : FALSE; } /** * Returns the conjunction of this and the specified formula. The effect * of this method is the same as calling this.compose(AND, formula). * @return this.compose(AND, formula) */ public final Formula and(Formula formula) { return compose(AND,formula); } /** * Returns the conjunction of this and the specified formula. The effect * of this method is the same as calling this.compose(OR, formula). * @return this.compose(OR, formula) */ public final Formula or(Formula formula) { return compose(OR,formula); } /** * Returns a formula that equates this and the specified formula. The effect * of this method is the same as calling this.compose(IFF, formula). * @return this.compose(IFF, formula) */ public final Formula iff(Formula formula) { return compose(IFF,formula); } /** * Returns the implication of the specified formula by this. The effect * of this method is the same as calling this.compose(IMPLIES, formula). * @return this.compose(IMPLIES, formula) */ public final Formula implies(Formula formula) { return compose(IMPLIES,formula); } /** * Returns the composition of this and the specified formula using the * given binary operator. * @return {f: Formula | f.left = this and f.right = formula and f.op = op } */ public final Formula compose(FormulaOperator op, Formula formula) { return new BinaryFormula(this, op, formula); } /** * Returns the conjunction of the given formulas. The effect of this method is the * same as calling compose(AND, formulas). * @return compose(AND, formulas) */ public static Formula and(Formula...formulas) { return compose(AND, formulas); } /** * Returns the conjunction of the given formulas. The effect of this method is the * same as calling compose(AND, formulas). * @return compose(AND, formulas) */ public static Formula and(Collection<? extends Formula> formulas) { return compose(AND, formulas); } /** * Returns the disjunction of the given formulas. The effect of this method is the * same as calling compose(OR, formulas). * @return compose(OR, formulas) */ public static Formula or(Formula...formulas) { return compose(OR, formulas); } /** * Returns the disjunction of the given formulas. The effect of this method is the * same as calling compose(OR, formulas). * @return compose(OR, formulas) */ public static Formula or(Collection<? extends Formula> formulas) { return compose(OR, formulas); } /** * Returns the composition of the given formulas using the given operator. * @requires formulas.length != 2 => op.nary() * @return * <pre> * formulas.length = 0 => constant(op=AND) else * formulas.length=1 => formulas[0] else * {e: Formula | e.children = formulas and e.op = this } * </pre> */ public static Formula compose(FormulaOperator op, Formula...formulas) { switch(formulas.length) { case 0 : switch(op) { case AND : return TRUE; case OR : return FALSE; default : throw new IllegalArgumentException("Expected at least one argument: " + Arrays.toString(formulas)); } case 1 : return formulas[0]; case 2 : return new BinaryFormula(formulas[0], op, formulas[1]); default : return new NaryFormula(op, Containers.copy(formulas, new Formula[formulas.length])); } } /** * Returns the composition of the given formulas using the given operator. * @requires formulas.size() != 2 => op.nary() * @return * <pre> * formulas.size() = 0 => constant(op=AND) else * formulas.size() = 1 => formulas.iterator().next() else * {e: Formula | e.children = formulas.toArray() and e.op = this } * </pre> */ public static Formula compose(FormulaOperator op, Collection<? extends Formula> formulas) { switch(formulas.size()) { case 0 : switch(op) { case AND : return TRUE; case OR : return FALSE; default : throw new IllegalArgumentException("Expected at least one argument: " + formulas); } case 1 : return formulas.iterator().next(); case 2 : final Iterator<? extends Formula> itr = formulas.iterator(); return new BinaryFormula(itr.next(), op, itr.next()); default : return new NaryFormula(op, formulas.toArray(new Formula[formulas.size()])); } } /** * Returns a formula that represents a universal quantification of this * formula over the given declarations. The effect of this method is the same * as calling this.quantify(ALL, decls). * @return this.quantify(ALL, decls) */ public final Formula forAll(Decls decls) { return quantify(ALL, decls); } /** * Returns a formula that represents an existential quantification of this * formula over the given declarations. The effect of this method is the same * as calling this.quantify(SOME, decls). * @return this.quantify(SOME, decls) */ public final Formula forSome(Decls decls) { return quantify(SOME, decls); } /** * Returns a quantification of this formula using the given quantifier over * the specified declarations. * @return {f: Formula | f.decls = decls and f.formula = this and f.quantifier = quantifer } */ public final Formula quantify(Quantifier quantifier, Decls decls) { return new QuantifiedFormula(quantifier, decls, this); } /** * Returns the comprehension expression constructed from this formula and * the given declarations. * @requires all d: decls.decls[int] | decl.variable.arity = 1 and decl.multiplicity = ONE * @return {e: Expression | e.decls = decls and e.formula = this } */ public final Expression comprehension(Decls decls) { return new Comprehension(decls,this); } /** * Returns the if expression constructed from this formula and the * specified then and else expressions. * @return {e: Expression | e.condition = this and e.thenExpr = thenExpr and e.elseExpr = elseExpr} */ public final Expression thenElse(Expression thenExpr, Expression elseExpr) { return new IfExpression(this, thenExpr, elseExpr); } /** * Returns the if expression constructed from this formula and the * specified then and else integer expressions. * @return {e: IntExpression | e.condition = this and e.thenExpr = thenExpr and e.elseExpr = elseExpr} */ public final IntExpression thenElse(IntExpression thenExpr, IntExpression elseExpr) { return new IfIntExpression(this, thenExpr, elseExpr); } /** * Returns the negation of this formula. * @return {f : NotFormula | f.formula = this } */ public final Formula not() { return new NotFormula(this); } /** * Accepts the given visitor and returns the result. * @see kodkod.ast.Node#accept(kodkod.ast.visitor.ReturnVisitor) */ public abstract <E, F, D, I> F accept(ReturnVisitor<E, F, D, I> visitor); }