protected void expandFrontier(final HashSetChartCell cell) {

    // connect edge as possible right non-term
    for (int start = 0; start < cell.start(); start++) {
      setSpanMaxEdgeFOM(chart.getCell(start, cell.start()), cell);
    }

    // connect edge as possible left non-term
    for (int end = cell.end() + 1; end <= chart.size(); end++) {
      setSpanMaxEdgeFOM(cell, chart.getCell(cell.end(), end));
    }
  }
  protected void visitCell(final HashSetChartCell cell) {
    final int start = cell.start(), end = cell.end();
    Collection<Production> possibleProds;
    ChartEdge edge;
    final ChartEdge[] bestEdges = new ChartEdge[grammar.numNonTerms()]; // inits to null

    // final int maxEdgesToAdd = (int) opts.param2;
    final int maxEdgesToAdd = Integer.MAX_VALUE;

    for (int mid = start + 1; mid <= end - 1; mid++) { // mid point
      final HashSetChartCell leftCell = chart.getCell(start, mid);
      final HashSetChartCell rightCell = chart.getCell(mid, end);
      for (final int leftNT : leftCell.getLeftChildNTs()) {
        for (final int rightNT : rightCell.getRightChildNTs()) {
          possibleProds = grammar.getBinaryProductionsWithChildren(leftNT, rightNT);
          if (possibleProds != null) {
            for (final Production p : possibleProds) {
              edge = chart.new ChartEdge(p, leftCell, rightCell);
              addEdgeToArray(edge, bestEdges);
            }
          }
        }
      }
    }

    addBestEdgesToChart(cell, bestEdges, maxEdgesToAdd);
  }
  protected void addBestEdgesToChart(
      final HashSetChartCell cell, final ChartEdge[] bestEdges, final int maxEdgesToAdd) {
    ChartEdge edge, unaryEdge;
    int numAdded = 0;

    final PriorityQueue<ChartEdge> agenda = new PriorityQueue<ChartEdge>();
    for (int i = 0; i < bestEdges.length; i++) {
      if (bestEdges[i] != null) {
        addEdgeToAgenda(bestEdges[i], agenda);
      }
    }

    while (agenda.isEmpty() == false && numAdded <= maxEdgesToAdd) {
      edge = agenda.poll();
      // addedEdge = cell.addEdge(edge);
      // if (addedEdge) {
      final int nt = edge.prod.parent;
      final float insideProb = edge.inside();
      if (insideProb > cell.getInside(edge.prod.parent)) {
        cell.updateInside(nt, insideProb);
        // System.out.println(" addingEdge: " + edge);
        numAdded++;
        // Add unary productions to agenda so they can compete with binary productions
        for (final Production p : grammar.getUnaryProductionsWithChild(edge.prod.parent)) {
          unaryEdge = chart.new ChartEdge(p, cell);
          addEdgeToAgenda(unaryEdge, agenda);
        }
      }
    }

    // TODO: should I decrease the maxEdgeFOM here according to the best edge NOT in the chart?
    // won't this just be overrun when we expand the frontier?
    if (agenda.isEmpty()) {
      maxEdgeFOM[cell.start()][cell.end()] = Float.NEGATIVE_INFINITY;
    } else {
      maxEdgeFOM[cell.start()][cell.end()] = agenda.peek().fom;
    }
  }
  protected void setSpanMaxEdgeFOM(
      final HashSetChartCell leftCell, final HashSetChartCell rightCell) {
    ChartEdge edge;
    final int start = leftCell.start(), end = rightCell.end();
    float bestFOM = maxEdgeFOM[start][end];

    // System.out.println(" setSpanMax: " + leftCell + " && " + rightCell);

    Collection<Production> possibleProds;
    for (final int leftNT : leftCell.getLeftChildNTs()) {
      for (final int rightNT : rightCell.getRightChildNTs()) {
        possibleProds = grammar.getBinaryProductionsWithChildren(leftNT, rightNT);
        if (possibleProds != null) {
          for (final Production p : possibleProds) {
            // final float prob = p.prob + leftCell.getInside(leftNT) +
            // rightCell.getInside(rightNT);
            edge = chart.new ChartEdge(p, leftCell, rightCell);
            // System.out.println(" considering: " + edge);
            if (edge.fom > bestFOM) {
              bestFOM = edge.fom;
            }
          }
        }
      }
    }

    if (bestFOM > maxEdgeFOM[start][end]) {
      final HashSetChartCell parentCell = chart.getCell(start, end);
      // if (maxEdgeFOM[start][end] > Float.NEGATIVE_INFINITY) {
      // spanAgenda.remove(parentCell);
      // }
      maxEdgeFOM[start][end] = bestFOM;
      parentCell.fom = bestFOM;
      // spanAgenda.add(parentCell);
      // System.out.println(" addingSpan: " + parentCell);
    }
  }