/******************************************************************************* * Copyright 2014 Felipe Takiyama * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package br.usp.poli.takiyama.common; import java.util.Iterator; import br.usp.poli.takiyama.prv.Binding; import br.usp.poli.takiyama.prv.Constant; import br.usp.poli.takiyama.prv.LogicalVariable; import br.usp.poli.takiyama.prv.StdLogicalVariable; import br.usp.poli.takiyama.prv.Substitution; import br.usp.poli.takiyama.prv.Term; /** * This class represents inequality constraints of the form X ≠ Y, where * X and Y are a Terms. * <p> * Inequalities with two constants, like t ≠ q, are invalid. If a method * detects a invalid constraint, it returns null. * </p> * * @author Felipe Takiyama * */ public final class InequalityConstraint extends AbstractConstraint { /* ************************************************************************ * Constructors * ************************************************************************/ /** * Creates the inequality constraint composed by the specified terms. * * @param firstTerm The left-hand side of the constraint * @param secondTerm The right-hand side of the constraint * @return An inequality constraint * @throws IllegalArgumentException if both terms are equal * @throws IllegalStateException if both terms are {@link Constant}s */ private InequalityConstraint(Term t1, Term t2) throws IllegalArgumentException, IllegalStateException { if (t1.equals(t2)) { // trying to create a constraint with equal terms throw new IllegalArgumentException(); } if (t1.isVariable() || t2.isVariable()) { firstTerm = t1; secondTerm = t2; } else { // trying to create a constraint with two constants throw new IllegalStateException(); } } /* ************************************************************************ * Static factories * ************************************************************************/ /** * Static factory of inequality constraints * * @param firstTerm The left-hand side of the constraint * @param secondTerm The right-hand side of the constraint * @return An inequality constraint * @throws IllegalArgumentException if both terms are {@link Constant}s or * if both terms are equal */ public static Constraint getInstance(Term firstTerm, Term secondTerm) { return new InequalityConstraint(firstTerm, secondTerm); } /* ************************************************************************ * Inherited methods * ************************************************************************/ @Override public Constraint apply(Substitution s) throws IllegalArgumentException, IllegalStateException { Term t1 = this.firstTerm; Term t2 = this.secondTerm; for (Iterator<LogicalVariable> it = s.getSubstitutedIterator(); it.hasNext(); ) { LogicalVariable replaced = it.next(); if (replaced.equals(t1)) { t1 = s.getReplacement(replaced); } if (replaced.equals(t2)) { t2 = s.getReplacement(replaced); } } return new InequalityConstraint(t1, t2); } @Override public boolean isConsistentWith(Binding b) { boolean isConsistent = false; if (firstTerm.isVariable() && secondTerm.isVariable()) { // Any binding will result in ambiguous constraint isConsistent = false; } else { if (isApplicable(b)) { if (b.secondTerm().isVariable()) { // Applying the binding results in ambiguous constraint isConsistent = false; } else { // Applies the binding and check whether the result is // a true sentence InequalityConstraint c = new InequalityConstraint(firstTerm, secondTerm); c.firstTerm = b.secondTerm(); isConsistent = !c.firstTerm.equals(c.secondTerm); } } else { // The binding is not applicable, thus the constraint // remains the same. isConsistent = true; } } return isConsistent; } /** * Returns true if the specified binding can be applied in this * constraint. * * @param b The binding to test applicability * @return true if the specified binding can be applied in this * constraint. */ private boolean isApplicable(Binding b) { return contains(b.firstTerm()); } /* ************************************************************************ * hashCode, equals and toString * ************************************************************************/ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof InequalityConstraint)) { return false; } InequalityConstraint other = (InequalityConstraint) obj; // tests A!=B == A!=B boolean direct = (((firstTerm == null) ? (other.firstTerm == null) : (firstTerm.equals(other.firstTerm))) && ((secondTerm == null) ? (other.secondTerm == null) : (secondTerm.equals(other.secondTerm)))); // tests A!=B == B!=A boolean inverse = (((firstTerm == null) ? (other.secondTerm == null) : (firstTerm.equals(other.secondTerm))) && ((secondTerm == null) ? (other.firstTerm == null) : (secondTerm.equals(other.firstTerm)))); return direct || inverse; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((firstTerm == null) ? 0 : firstTerm.hashCode()); result = prime * result + ((secondTerm == null) ? 0 : secondTerm.hashCode()); result = prime * 5; // otherwise EqualityConstraint with same terms would have same hash return result; } @Override public String toString() { return firstTerm.toString() + " != " + secondTerm.toString(); } /** * @deprecated * Apply the substitution in this inequality. * The following rules apply:<br> * <li> X ≠ Y and X/q returns Y ≠ q * <li> X ≠ Y and Y/q returns X ≠ q * <li> X ≠ Y and X/W returns W ≠ Y * <li> X ≠ Y and Y/W returns X ≠ W * <li> X ≠ t and X/q returns <b>null</b> * <li> X ≠ t and Y/q returns X ≠ t * <li> X ≠ t and X/W returns W ≠ t * <li> X ≠ t and Y/W returns X ≠ t * <br> * @param substitution The substitution to apply on the constraint * @return The constraint that results from the application of the * specified substitution to this constraint, following the rules * specified above. */ public Constraint applySubstitution(Binding substitution) { if (firstTerm.equals(substitution.firstTerm())) { // looks ugly if (secondTerm instanceof StdLogicalVariable && substitution.secondTerm() instanceof Constant) { // X!=Y && X/q return new InequalityConstraint((LogicalVariable) secondTerm, substitution.secondTerm()); } else if (secondTerm instanceof Constant && substitution.secondTerm() instanceof Constant) { // X!=t && X/q return null; } else { return new InequalityConstraint((LogicalVariable) substitution.secondTerm(), secondTerm); } } else if (secondTerm.equals(substitution.firstTerm())) { return new InequalityConstraint(firstTerm, substitution.secondTerm()); } else { return this; } } }