class UnitInterferenceGraph {

    // Maps a local to its interfering locals.
    final Map<Local, Set<Local>> localToLocals;
    final List<Local> locals;

    public UnitInterferenceGraph(Body body, Map<Local, ? extends Object> localToGroup, LiveLocals liveLocals,
        ExceptionalUnitGraph unitGraph) {

      this.locals = new ArrayList<Local>(body.getLocals());
      this.localToLocals = new HashMap<Local, Set<Local>>(body.getLocalCount() * 2 + 1, 0.7f);

      // Go through code, noting interferences
      for (Unit unit : body.getUnits()) {
        List<ValueBox> defBoxes = unit.getDefBoxes();

        // Note interferences if this stmt is a definition
        if (!defBoxes.isEmpty()) {
          // Only one def box is supported
          if (defBoxes.size() != 1) {
            throw new RuntimeException("invalid number of def boxes");
          }

          // Remove those locals that are only live on exceptional flows.
          // If we have code like this:
          // a = 42
          // b = foo()
          // catch -> print(a)
          // we can transform it to:
          // a = 42
          // a = foo()
          // catch -> print(a)
          // If an exception is thrown, at the second assignment, the
          // assignment will not actually happen and "a" will be unchanged.

          // SA, 2018-02-02: The above is only correct if there is
          // nothing else in the trap. Take this example:
          // a = 42
          // b = foo()
          // throw new VeryBadException()
          // catch -> print(a)
          // In that case, the value of "b" **will** be changed before
          // we reach the handler (assuming that foo() does not already
          // throw the exception). We may want to have a more complex
          // reasoning here some day, but I'll leave it as is for now.

          Value defValue = defBoxes.get(0).getValue();
          if (defValue instanceof Local) {
            Local defLocal = (Local) defValue;

            Set<Local> liveLocalsAtUnit = new HashSet<Local>();
            for (Unit succ : unitGraph.getSuccsOf(unit)) {
              liveLocalsAtUnit.addAll(liveLocals.getLiveLocalsBefore(succ));
            }

            for (Local otherLocal : liveLocalsAtUnit) {
              if (localToGroup.get(otherLocal).equals(localToGroup.get(defLocal))) {
                setInterference(defLocal, otherLocal);
              }
            }
          }
        }
      }
    }

    public List<Local> getLocals() {
      return locals;
    }

    public void setInterference(Local l1, Local l2) {<FILL_FUNCTION_BODY>}

    public int getInterferenceCount(Local l) {
      Set<Local> localSet = localToLocals.get(l);
      return localSet == null ? 0 : localSet.size();
    }

    public Local[] getInterferencesOf(Local l) {
      Set<Local> localSet = localToLocals.get(l);
      if (localSet == null) {
        return null;
      } else {
        return localSet.toArray(new Local[localSet.size()]);
      }
    }
  }

      // We need the mapping in both directions
      // l1 -> l2
      Set<Local> locals = localToLocals.get(l1);
      if (locals == null) {
        locals = new ArraySet<Local>();
        localToLocals.put(l1, locals);
      }
      locals.add(l2);

      // l2 -> l1
      locals = localToLocals.get(l2);
      if (locals == null) {
        locals = new ArraySet<Local>();
        localToLocals.put(l2, locals);
      }
      locals.add(l1);
    
<no_super_class>