/**
  * Extracts the morphism from rule nodes to input graph nodes corresponding to the transition's
  * input parameters.
  *
  * @return if {@code null}, the binding cannot be constructed and so the rule cannot match
  */
 private RuleToHostMap extractBinding(Step step) {
   RuleToHostMap result = this.state.getGraph().getFactory().createRuleToHostMap();
   Object[] sourceValues = this.state.getActualValues();
   for (Assignment assign : step.getEnterAssignments()) {
     sourceValues = assign.compute(sourceValues);
   }
   for (Pair<Var, Binding> entry : step.getRuleSwitch().getCallBinding()) {
     Binding bind = entry.two();
     HostNode value;
     if (bind == null) {
       // this corresponds to an output parameter of the call
       continue;
     }
     switch (bind.getSource()) {
       case CONST:
         value = bind.getValue().getNode();
         break;
       case VAR:
         value = Valuator.get(sourceValues, bind);
         break;
       default:
         assert false;
         value = null;
     }
     RuleNode ruleNode = entry.one().getRuleNode();
     if (isCompatible(ruleNode, value)) {
       result.putNode(ruleNode, value);
     } else {
       result = null;
       break;
     }
   }
   return result;
 }
 /**
  * Returns the set of matching events for a given control step.
  *
  * @param step the control step for which matches are to be found; non-{@code null}
  */
 public MatchResultSet computeMatches(final Step step) {
   final MatchResultSet result = new MatchResultSet();
   if (DEBUG) {
     System.out.printf("Matches for %s, %s%n  ", this.state, this.state.getGraph());
   }
   assert step != null;
   // there are three reasons to want to use the parent matches: to
   // save matching time, to reuse added nodes, and to find confluent
   // diamonds. The first is only relevant if the rule is not (re)enabled,
   // the third only if the parent match target is already closed
   final boolean isDisabled = isDisabled(step.getRuleCall());
   boolean isModifying = step.isModifying();
   if (!isDisabled) {
     for (GraphTransition trans : this.parentTransMap) {
       if (trans instanceof RuleTransition) {
         RuleTransition ruleTrans = (RuleTransition) trans;
         if (ruleTrans.getEvent().getRule().equals(step.getRule())) {
           MatchResult match = ruleTrans.getKey();
           if (isModifying) {
             // we can reuse the event but not the control step
             match = new MatchResult(match.getEvent(), step);
           }
           result.add(match);
           if (DEBUG) {
             System.out.print(" T" + System.identityHashCode(trans.getEvent()));
           }
         }
       }
     }
   }
   if (isDisabled || isEnabled(step.getRuleCall())) {
     // the rule was possibly enabled afresh, so we have to add the fresh
     // matches
     RuleToHostMap boundMap = extractBinding(step);
     if (boundMap != null) {
       final Record record = this.record;
       Visitor<Proof, Boolean> eventCollector =
           new Visitor<Proof, Boolean>(false) {
             @Override
             protected boolean process(Proof object) {
               RuleEvent event = record.getEvent(object);
               // only look up the event in the parent map if
               // the rule was disabled, as otherwise the result
               // already contains all relevant parent results
               MatchResult match = new MatchResult(event, step);
               if (isDisabled) {
                 match = getParentTrans(match);
               }
               result.add(match);
               if (DEBUG) {
                 System.out.print(" E" + System.identityHashCode(match.getEvent()));
                 checkEvent(match.getEvent());
               }
               setResult(true);
               return true;
             }
           };
       step.getRule().traverseMatches(this.state.getGraph(), boundMap, eventCollector);
     }
   }
   if (DEBUG) {
     System.out.println();
   }
   return result;
 }