/** Returns a representation of a region in dot-format (graphviz). */
  public String regionToDot(Region r) {
    nodeCounter = 2; // counter for nodes, values 0 and 1 are used for nodes FALSE and TRUE
    Map<Region, Integer> cache = new HashMap<>(); // map for same regions
    StringBuilder str = new StringBuilder("digraph G {\n");

    // make nodes for FALSE and TRUE
    if (!r.isTrue()) {
      str.append("0 [shape=box, label=\"0\", style=filled, shape=box, height=0.3, width=0.3];\n");
      cache.put(makeFalse(), 0);
    }
    if (!r.isFalse()) {
      str.append("1 [shape=box, label=\"1\", style=filled, shape=box, height=0.3, width=0.3];\n");
      cache.put(makeTrue(), 1);
    }

    regionToDot(r, str, cache);

    str.append("}\n");
    return str.toString();
  }
  private void dumpRegion(Region r, Appendable out) throws IOException {
    if (regionMap.containsValue(r)) {
      out.append(regionMap.inverse().get(r));

    } else if (r.isFalse()) {
      out.append("FALSE");

    } else if (r.isTrue()) {
      out.append("TRUE");

    } else {
      Triple<Region, Region, Region> triple = delegate.getIfThenElse(r);
      String predName = regionMap.inverse().get(triple.getFirst());

      Region trueBranch = triple.getSecond();
      Region falseBranch = triple.getThird();

      if (trueBranch.isFalse()) {
        assert !falseBranch.isFalse();
        // only falseBranch is present
        out.append("!").append(predName).append(" & ");
        dumpRegion(falseBranch, out);

      } else if (falseBranch.isFalse()) {
        // only trueBranch is present
        out.append(predName).append(" & ");
        dumpRegion(trueBranch, out);

      } else {
        // both branches present
        out.append("((").append(predName).append(" & ");
        dumpRegion(trueBranch, out);
        out.append(") | (").append("!").append(predName).append(" & ");
        dumpRegion(falseBranch, out);
        out.append("))");
      }
    }
  }