/** * Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at> */ package at.iaik.suraq.util.chain; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import at.iaik.suraq.exceptions.WrongFunctionTypeException; import at.iaik.suraq.exceptions.WrongNumberOfParametersException; import at.iaik.suraq.proof.VeritProofNode; import at.iaik.suraq.smtlib.formula.DomainEq; import at.iaik.suraq.smtlib.formula.DomainTerm; import at.iaik.suraq.smtlib.formula.Formula; import at.iaik.suraq.smtlib.formula.NotFormula; import at.iaik.suraq.smtlib.formula.UninterpretedFunction; import at.iaik.suraq.smtlib.formula.UninterpretedFunctionInstance; import at.iaik.suraq.util.Justification; import at.iaik.suraq.util.Util; /** * An element in a transitivity-congruence chain. * * @author Georg Hofferek <georg.hofferek@iaik.tugraz.at> * */ public class TransitivityCongruenceChainElement { /** * The term that this element represents. */ private final DomainTerm term; /** * Pointer to next element in the chain. */ private TransitivityCongruenceChainElement next = null; /** * The equality justifying the link to the next element, or * <code>null</code> if it is a congruence link. */ private DomainEq equalityJustification = null; /** * The list of chains justifying the link to the next element, of * <code>null</code> if it is an equality link. */ private List<TransitivityCongruenceChain> congruenceJustification = null; /** * Used to avoid creating symmetric literals. */ private final VeritProofNode proofNode; /** * * Constructs a new <code>TransitivityCongruenceChainElement</code>. * * @param term */ protected TransitivityCongruenceChainElement(DomainTerm term, VeritProofNode proofNode) { assert (term != null); assert (proofNode != null); this.term = term; this.proofNode = proofNode; } /** * Constructs a new <code>TransitivityCongruenceChainElement</code>. * * @param element */ protected TransitivityCongruenceChainElement( TransitivityCongruenceChainElement element) { this.term = element.term; this.next = element.next; this.equalityJustification = element.equalityJustification; this.congruenceJustification = element.congruenceJustification; this.proofNode = element.proofNode; } /** * Tries to attach the given <code>equality</code> to this chain element. If * <code>next</code> is not <code>null</code> attachment will fail. * * @param equality * @return <code>true</code> if the given equality could be attached. */ protected boolean tryAttach(DomainEq equality, VeritProofNode proofNode) { if (next != null) return false; assert (equality != null); assert (equality.isEqual()); assert (equality.getTerms().size() == 2); assert (equality.getTerms().get(0) instanceof DomainTerm); assert (equality.getTerms().get(1) instanceof DomainTerm); if (this.term.equals(equality.getTerms().get(0))) { assert (next == null); this.next = new TransitivityCongruenceChainElement( (DomainTerm) equality.getTerms().get(1), proofNode); assert (next != null); this.equalityJustification = equality; assert (congruenceJustification == null); return true; } if (this.term.equals(equality.getTerms().get(1))) { assert (next == null); this.next = new TransitivityCongruenceChainElement( (DomainTerm) equality.getTerms().get(0), proofNode); assert (next != null); this.equalityJustification = equality; assert (congruenceJustification == null); return true; } assert (!this.term.equals(equality.getTerms().get(0)) && !this.term .equals(equality.getTerms().get(1))); assert (next == null); assert (equalityJustification == null); assert (congruenceJustification == null); return false; } protected boolean tryAttach(UninterpretedFunctionInstance nextTerm, List<TransitivityCongruenceChain> justification, VeritProofNode proofNode) { if (next != null) return false; assert (next == null); assert (equalityJustification == null); assert (congruenceJustification == null); assert (nextTerm != null); assert (justification != null); assert (!justification.isEmpty()); if (!(this.term instanceof UninterpretedFunctionInstance)) return false; if (!nextTerm.getFunction().equals( ((UninterpretedFunctionInstance) this.term).getFunction())) return false; if (!checkJustification(justification, ((UninterpretedFunctionInstance) this.term).getParameters(), nextTerm.getParameters())) return false; this.congruenceJustification = new ArrayList<TransitivityCongruenceChain>( justification); this.next = new TransitivityCongruenceChainElement(nextTerm, proofNode); assert (next != null); assert (congruenceJustification != null); assert (equalityJustification == null); return true; } /** * Convenience method that constructs the <code>nextTerm</code> * automatically. * * @param justification * @return <code>true</code> if attachment was successful */ protected boolean tryAttach( List<TransitivityCongruenceChain> justification, VeritProofNode proofNode) { if (!(this.term instanceof UninterpretedFunctionInstance)) return false; UninterpretedFunction function = ((UninterpretedFunctionInstance) this.term) .getFunction(); if (justification.size() != function.getNumParams()) return false; List<DomainTerm> parameters = new ArrayList<DomainTerm>(); for (TransitivityCongruenceChain chain : justification) parameters.add(chain.getEndTerm()); try { UninterpretedFunctionInstance nextTerm = UninterpretedFunctionInstance .create(function, parameters); return tryAttach(nextTerm, justification, proofNode); } catch (WrongNumberOfParametersException exc) { return false; } catch (WrongFunctionTypeException exc) { return false; } } /** * @param justification * @param parameters1 * @param parameters2 * @return */ private boolean checkJustification( List<TransitivityCongruenceChain> justification, List<DomainTerm> parameters1, List<DomainTerm> parameters2) { if (justification.size() != parameters1.size()) return false; if (justification.size() != parameters2.size()) return false; assert (parameters1.size() == parameters2.size()); for (int count = 0; count < justification.size(); count++) { TransitivityCongruenceChain currentChain = justification.get(count); if (!currentChain.isComplete()) return false; if (!currentChain.getStart().getTerm() .equals(parameters1.get(count))) return false; if (!currentChain.getTarget().equals(parameters2.get(count))) return false; } return true; } /** * * @return a set of formulas used as link in this element. */ protected Set<Formula> usedLiterals() { Set<Formula> result = new HashSet<Formula>(); if (equalityJustification != null) { assert (congruenceJustification == null); result.add(equalityJustification); } else if (congruenceJustification != null) { assert (equalityJustification == null); assert (!congruenceJustification.isEmpty()); for (TransitivityCongruenceChain chain : congruenceJustification) result.addAll(chain.usedLiterals()); } return result; } /** * @return the <code>term</code> */ public DomainTerm getTerm() { return term; } /** * @return the <code>next</code> */ public TransitivityCongruenceChainElement getNext() { return next; } /** * * @return <code>true</code> if there is a next element. */ public boolean hasNext() { return next != null; } /** * @return the <code>equalityJustification</code> */ public DomainEq getEqualityJustification() { return equalityJustification; } /** * @return the <code>congruenceJustification</code> */ public List<TransitivityCongruenceChain> getCongruenceJustification() { return congruenceJustification; } /** * @return <code>true</code> if the term of this element is global. */ public boolean isGlobal() { Set<Integer> partitions = term.getPartitionsFromSymbols(); partitions.remove(-1); return partitions.isEmpty(); } /** * */ protected void makeNextNull() { this.next = null; this.congruenceJustification = null; this.equalityJustification = null; } /** * */ protected void attachReflexivity() { assert (this.next == null); assert (this.equalityJustification == null); assert (this.congruenceJustification == null); this.next = new TransitivityCongruenceChainElement(this.term, this.proofNode); this.equalityJustification = Util.createReflexivity(this.term); } /** * @param patch */ protected void makeLeftSplice(TransitivityCongruenceChainElement patch) { assert (this.term.equals(patch.term)); this.next = patch.next; this.equalityJustification = patch.equalityJustification; this.congruenceJustification = patch.congruenceJustification; assert (this.congruenceJustification == null ^ this.equalityJustification == null); } /** * @param patch */ protected void makeRightSplice(TransitivityCongruenceChainElement patch) { assert (this.term.equals(patch.term)); patch.next = this.next; patch.equalityJustification = this.equalityJustification; patch.congruenceJustification = this.congruenceJustification; assert ((patch.congruenceJustification == null ^ patch.equalityJustification == null) || (patch.next == null && patch.congruenceJustification == null && patch.equalityJustification == null)); } /** * Called on the first occurrence, shortcuts the way to the successor of the * given second occurrence. * * @param secondOccurrence */ protected void makeShortcut( TransitivityCongruenceChainElement secondOccurrence) { assert (this.term.equals(secondOccurrence.term)); this.next = secondOccurrence.next; this.congruenceJustification = secondOccurrence.congruenceJustification; this.equalityJustification = secondOccurrence.equalityJustification; } /** * @return the set of partitions formed by all symbols in this chain * element. */ public Set<Integer> getPartitionsFromSymbols() { Set<Integer> result = new HashSet<Integer>(); result.addAll(term.getPartitionsFromSymbols()); if (equalityJustification != null) result.addAll(equalityJustification.getPartitionsFromSymbols()); if (congruenceJustification != null) { for (TransitivityCongruenceChain chain : congruenceJustification) result.addAll(chain.getPartitionsFromSymbols()); } return result; } /** * * @return the partition of the term of this element. */ public int getTermPartition() { Set<Integer> partitions = this.term.getPartitionsFromSymbols(); if (partitions.size() > 1) partitions.remove(-1); assert (partitions.size() == 1); return partitions.iterator().next(); } /** * @return */ public Justification getJustficiation() { if (equalityJustification == null ^ congruenceJustification == null) { if (equalityJustification == null) { return new Justification(congruenceJustification); } else { return new Justification(equalityJustification); } } else return null; } /** * @return */ public boolean isCongruenceOfLocalFunctionOverOtherPartition() { if (this.congruenceJustification == null) return false; if (this.equalityJustification != null) return false; if (this.term == null) return false; if (!(this.term instanceof UninterpretedFunctionInstance)) return false; UninterpretedFunctionInstance term1 = (UninterpretedFunctionInstance) this.term; if (this.next == null) return false; if (next.term == null) return false; if (!(next.term instanceof UninterpretedFunctionInstance)) return false; UninterpretedFunctionInstance term2 = (UninterpretedFunctionInstance) next.term; if (!term1.getFunction().equals(term2.getFunction())) return false; if (Util.isGlobal(term1)) return false; if (Util.isGlobal(term2)) return false; Set<Integer> partitions = this.getPartitionsFromSymbols(); partitions.remove(-1); if (partitions.size() <= 1) return false; return true; } /** * @param partitions * @return */ public boolean isCongruenceOverOtherPartition(Set<Integer> partitions) { assert (partitions != null); assert (!partitions.contains(-1)); assert (partitions.size() <= 1); if (this.congruenceJustification == null) return false; if (this.equalityJustification != null) return false; if (this.term == null) return false; if (!(this.term instanceof UninterpretedFunctionInstance)) return false; UninterpretedFunctionInstance term1 = (UninterpretedFunctionInstance) this.term; if (this.next == null) return false; if (next.term == null) return false; if (!(next.term instanceof UninterpretedFunctionInstance)) return false; UninterpretedFunctionInstance term2 = (UninterpretedFunctionInstance) next.term; if (!term1.getFunction().equals(term2.getFunction())) return false; Set<Integer> partitionsOfThis = this.getPartitionsFromSymbols(); partitionsOfThis.remove(-1); if (partitionsOfThis.size() != 1) return false; partitionsOfThis.addAll(partitions); if (partitionsOfThis.size() == 1) return false; return true; } /** * For a congruence link to be colorable, all equalities for the parameters * as well as the implied literal (equality between function instances) have * to be colorable with one color. Internally, the equalities for the * parameters may use different colors, but they must be "summarizable" by * literals of one color. This method does <strong>not</strong> do a * recursive check! * * @return <code>true</code> iff this element has a colorable congruence * justification. */ public boolean hasColorableCongruenceJustification() { assert (this.congruenceJustification != null); assert (this.equalityJustification == null); Set<Integer> partitions = this.term.getPartitionsFromSymbols(); partitions.addAll(this.next.term.getPartitionsFromSymbols()); for (TransitivityCongruenceChain chain : this.congruenceJustification) { partitions.add(chain.getStartPartition()); partitions.add(chain.getEndPartition()); } partitions.remove(-1); return partitions.size() <= 1; } /** * Returns the literal that represents the link between this element and the * next. (Thus, <code>next</code> may not be <code>null</code>!) If this * literal already occurred in the original proof node, it is guaranteed * that it is not returned in reverse. If it is not in the original node, * the order of the terms will be as in the chain: first the term of * <code>this</code>, then the term of <code>this.next</code>. * * @return the literal linking this element to the next, in positive phase. */ public DomainEq getLiteral() { assert (this.next != null); List<DomainTerm> terms = new ArrayList<DomainTerm>(2); terms.add(this.term); terms.add(this.next.term); DomainEq literal = DomainEq.create(terms, true); DomainEq reversedLiteral = (DomainEq) Util.reverseEquality(literal); NotFormula invertedLiteral = NotFormula.create(literal); NotFormula invertedReversedLiteral = NotFormula.create(reversedLiteral); List<Formula> conclusions = proofNode.getLiteralConclusions(); if (conclusions.contains(literal) || conclusions.contains(invertedLiteral)) { assert (!conclusions.contains(reversedLiteral) && !conclusions .contains(invertedReversedLiteral)); return literal; } if (conclusions.contains(reversedLiteral) || conclusions.contains(invertedReversedLiteral)) { assert (!conclusions.contains(literal) && !conclusions .contains(invertedLiteral)); return reversedLiteral; } // This is a new literal, not occurring in the proof node. // return it in the order of the chain, by default. return literal; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int result = term.hashCode(); return result; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (this.hashCode() != obj.hashCode()) return false; if (!(obj instanceof TransitivityCongruenceChainElement)) return false; TransitivityCongruenceChainElement other = (TransitivityCongruenceChainElement) obj; if (!this.term.equals(other.term)) return false; if (!(this.equalityJustification == null ? other.equalityJustification == null : this.equalityJustification .equals(other.equalityJustification))) return false; if (!(this.congruenceJustification == null ? other.congruenceJustification == null : this.congruenceJustification .equals(other.congruenceJustification))) return false; return true; } }