package x10.constraint.visitors; import java.util.HashMap; import java.util.Map; import x10.constraint.XField; import x10.constraint.xnative.XNativeField; import x10.constraint.xnative.XNativeTerm; import x10.constraint.XTerm; import x10.constraint.XTerm.TermVisitor; import x10.constraint.XVar; /** * An XGraphVisitor may visit the graph underlying a representation of * a constraint <tt>c</tt>. * * <p> The visitor will see the atomic formulas * and the constraints <tt>s==t</tt> and <tt>s!=t</tt> stored in the graph in * some order. The set <tt>S</tt> of constraints seen will be sufficient to * entail <tt>c</tt>. No guarantee is made that <tt>S</tt> is "minimal" in some * sense. * * <p>The visitor may return false from any visit * method to terminate further traversal of the graph. * @author vj * */ public abstract class XGraphVisitor { Map<XTerm, XTerm> eqvVarRep; protected void addVarRep(XTerm eqv, XTerm rep) { if (eqvVarRep == null) eqvVarRep = new HashMap<XTerm, XTerm>(); if (eqvVarRep.get(eqv)==null) eqvVarRep.put(eqv, rep); } /** * Returns null unless eqvVarRep is set, and maps eqv to some value v. * In that case, returns the dereferenced version of v. That is, * a value v such that eqvVarRep(v)==null, or * ! eqvVarRep(v).hasEQV(). * @param eqv * @return */ protected XTerm varRep(XTerm eqv) { if (eqvVarRep == null) return null; XTerm result = eqvVarRep.get(eqv); if (result == null) return null; while (result.hasEQV()) { XTerm temp = eqvVarRep.get(result); if (temp == null) return result; result = temp; } return result; } /** Get the nf for eqv, given the information * in the hash table. The normal form for e.f1...fn is x.f1...fn * if e is bound to x in the table. */ protected XTerm nf(XTerm eqv) { XTerm z = varRep(eqv); if (z != null) return z; if (eqv instanceof XField<?> ) { XField<?> t = (XField<?>) eqv; XTerm rt = t.receiver(); XTerm tz =nf((XTerm)rt); if (tz == null) return null; return (XTerm)t.copyReceiver((XVar) tz); } return null; } boolean hideEQV; boolean hideFake; protected XGraphVisitor(boolean hideEQV, boolean hideFake) { this.hideEQV=hideEQV; this.hideFake=hideFake; } /** * Visiting the graph encounters a formula t. * Process this information. * Return false if the visit should be aborted. * @param t -- the formula encountered. * @return false -- the visit should be terminated. */ protected abstract boolean visitAtomicFormula(XTerm t); public boolean rawVisitAtomicFormula(XTerm t) { return visitAtomicFormula(t); } /** * Visiting the graph encounters t1 == t2. * Process this information. * Return false if the visit should be aborted. * @param t1 -- * @param t2 -- * @return false -- the visit should be terminated. */ protected abstract boolean visitEquals(XTerm t1, XTerm t2); protected void addEQVBinding(final XTerm t1, final XTerm t2) { if (eqvVarRep!=null) { TermVisitor tv = new TermVisitor() { public XTerm visit(XTerm t) {return t.equals(t1) ? t2: null;} }; for ( Map.Entry<XTerm,XTerm> t: eqvVarRep.entrySet()) { XTerm src = t.getKey(); XTerm dest = t.getValue(); XTerm tp = (XTerm)src.accept(tv); if ((! tp.hasEQV()) && (! dest.hasEQV())) visitEquals(tp, dest); } } addVarRep(t1, t2); } public boolean rawVisitEquals(XTerm t1, XTerm t2) { //assert t1 != null && t2 != null; if (hideEQV) { if (t1.hasEQV()) { XTerm t1b = nf(t1); if (t1b != null) t1=t1b; } if (t2.hasEQV()) { XTerm t2b = nf(t2); if (t2b != null) t2 = t2b; } if (t1.hasEQV()) {addEQVBinding(t1, t2);return true;} if (t2.hasEQV()) {addEQVBinding(t2, t1);return true;} } if (hideFake && t1 instanceof XNativeField && ((XNativeField<?>) t1).isHidden()) return true; if (hideFake && t2 instanceof XNativeField && ((XNativeField<?>) t2).isHidden()) return true; return visitEquals(t1, t2); } /** * Visiting the graph encounters t1 != t2. * Process this information. * Return false if the visit should be aborted. * @param t -- the formula encountered. * @return false -- the visit should be terminated. */ protected abstract boolean visitDisEquals(XTerm t1, XTerm t2); public boolean rawVisitDisEquals(XTerm t1, XTerm t2) { assert t1 != null && t2 != null; if (hideEQV) { if (t1.hasEQV()) { XTerm t1b = nf(t1); if (t1b != null) { t1=t1b; } } if (t2.hasEQV()) { XTerm t2b = nf(t2); if (t2b != null) t2 = t2b; } if (t1.hasEQV()) { // ugh. Of course, cannot add t1 --> t2 when we are processing t1 != t2!!! // addVarRep(t1, t2); // Ignoring is ok. If we have a term s that is not EQV that is // bound to this, then s != t2 will be processed when we // come through. Fortunately, != is not transitive so we do // not have to worry about handling s != t, u != t where // t is an eqv, but s and u are not. No implicit constraint // can be deduced between s and u from the above. return true; } if (t2.hasEQV()) { // addVarRep(t2, t1); return true; } } if (hideFake && t1 instanceof XNativeField && ((XNativeField<?>) t1).isHidden()) return true; if (hideFake && t2 instanceof XNativeField && ((XNativeField<?>) t2).isHidden()) return true; return visitDisEquals(t1, t2); } }