protected List<Tree> extractBestParses(int goal, int start, int end) {
    // find sources of inside score
    // no backtraces so we can speed up the parsing for its primary use
    double bestScore = iScore[start][end][goal];
    String goalStr = (String) stateNumberer.object(goal);
    //System.out.println("Searching for "+goalStr+" from "+start+" to "+end+" scored "+bestScore);
    // check tags
    if (end - start == 1 && tagNumberer.hasSeen(goalStr)) {
      IntTaggedWord tagging = new IntTaggedWord(words[start], tagNumberer.number(goalStr));
      float tagScore = lex.score(tagging, start);
      if (tagScore > Float.NEGATIVE_INFINITY || floodTags) {
        // return a pre-terminal tree
        String wordStr = (String) wordNumberer.object(words[start]);
        Tree wordNode = tf.newLeaf(wordStr);
        Tree tagNode = tf.newTreeNode(goalStr, Collections.singletonList(wordNode));
        //System.out.println("Tag node: "+tagNode);
        return Collections.singletonList(tagNode);
      }
    }
    // check binaries first
    List<Tree> bestTrees = new ArrayList<Tree>();
    for (int split = start + 1; split < end; split++) {
      for (Iterator<BinaryRule> binaryI = bg.ruleIteratorByParent(goal); binaryI.hasNext(); ) {
        BinaryRule br = binaryI.next();
        double score = br.score + iScore[start][split][br.leftChild] + iScore[split][end][br.rightChild];
        if (matches(score, bestScore)) {
          // build binary split
          List<Tree> leftChildTrees = extractBestParses(br.leftChild, start, split);
          List<Tree> rightChildTrees = extractBestParses(br.rightChild, split, end);
          // System.out.println("Found a best way to build " + goalStr + "(" +
          //                 start + "," + end + ") with " +
          //                 leftChildTrees.size() + "x" +
          //                 rightChildTrees.size() + " ways to build.");
          for (Tree leftChildTree : leftChildTrees) {
            for (Tree rightChildTree : rightChildTrees) {
              List<Tree> children = new ArrayList<Tree>();
              children.add(leftChildTree);
              children.add(rightChildTree);
              Tree result = tf.newTreeNode(goalStr, children);
              //System.out.println("Binary node: "+result);
              bestTrees.add(result);
            }
          }
        }
      }
    }
    // check unaries
    for (Iterator<UnaryRule> unaryI = ug.ruleIteratorByParent(goal); unaryI.hasNext(); ) {
      UnaryRule ur = unaryI.next();
      double score = ur.score + iScore[start][end][ur.child];
      if (ur.child != ur.parent && matches(score, bestScore)) {
        // build unary
        List<Tree> childTrees = extractBestParses(ur.child, start, end);
        for (Tree childTree : childTrees) {
          Tree result = tf.newTreeNode(goalStr, Collections.singletonList(childTree));
          //System.out.println("Unary node: "+result);
          bestTrees.add(result);
        }
      }
    }
    if (bestTrees.isEmpty()) {
      System.err.println("Warning: no parse found in ExhaustivePCFGParser.extractBestParse: failing on: [" + start + ", " + end + "] looking for " + goalStr);
    }
    return bestTrees;
  }