*          of the parser on the treebank.
   */
  public double testOnTreebank(Treebank testTreebank) {
    System.err.println("Testing on treebank");
    Timing treebankTotalTtimer = new Timing();
    TreePrint treePrint = getTreePrint();
    TreebankLangParserParams tlpParams = op.tlpParams;
    TreebankLanguagePack tlp = op.langpack();
    PrintWriter pwOut = tlpParams.pw();
    PrintWriter pwErr = tlpParams.pw(System.err);
    if (Test.verbose) {
      pwErr.print("Testing ");
      pwErr.println(testTreebank.textualSummary(tlp));
    }
    if (Test.evalb) {
      EvalbFormatWriter.initEVALBfiles(tlpParams);
    }

    PrintWriter pwo = null;
    if (Test.writeOutputFiles) {
      String fname = Test.outputFilesPrefix + "." + Test.outputFilesExtension;
      try {
        pwo = op.tlpParams.pw(new FileOutputStream(fname));
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }

    PrintWriter statsPwo = null;
    if(Test.outputkBestEquivocation != null) {
      try {
        statsPwo = op.tlpParams.pw(new FileOutputStream(Test.outputkBestEquivocation));
      } catch(IOException ioe) {
        ioe.printStackTrace();
      }
    }

    TreeTransformer tc = tlpParams.collinizer();
    TreeTransformer br = new BoundaryRemover();

    // evaluation setup
    boolean runningAverages = Boolean.parseBoolean(Test.evals.getProperty("runningAverages"));
    boolean summary = Boolean.parseBoolean(Test.evals.getProperty("summary"));
    boolean tsv = Boolean.parseBoolean(Test.evals.getProperty("tsv"));
    tlpParams.setupForEval();
    subcategoryStripper = tlpParams.subcategoryStripper();

    AbstractEval pcfgLB = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgLB"))) {
      pcfgLB = new Evalb("pcfg LP/LR", runningAverages);
    }
    LeafAncestorEval pcfgLA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgLA"))) {
      pcfgLA = new LeafAncestorEval("pcfg LeafAncestor");
    }
    AbstractEval pcfgCB = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgCB"))) {
      pcfgCB = new Evalb.CBEval("pcfg CB", runningAverages);
    }
    AbstractEval pcfgDA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgDA"))) {
      pcfgDA = new DependencyEval("pcfg DA", runningAverages, tlp.punctuationWordAcceptFilter());
    }
    AbstractEval pcfgTA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgTA"))) {
      pcfgTA = new TaggingEval("pcfg Tag", runningAverages, pd.lex);
    }
    AbstractEval depDA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("depDA"))) {
      depDA = new DependencyEval("dep DA", runningAverages, tlp.punctuationWordAcceptFilter());
    }
    AbstractEval depTA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("depTA"))) {
      depTA = new TaggingEval("dep Tag", runningAverages, pd.lex);
    }
    AbstractEval factLB = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factLB"))) {
      factLB = new Evalb("factor LP/LR", runningAverages);
    }
    LeafAncestorEval factLA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factLA"))) {
      factLA = new LeafAncestorEval("factor LeafAncestor");
    }
    AbstractEval factCB = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factCB"))) {
      factCB = new Evalb.CBEval("fact CB", runningAverages);
    }
    AbstractEval factDA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factDA"))) {
      factDA = new DependencyEval("factor DA", runningAverages, tlp.punctuationWordAcceptFilter());
    }
    AbstractEval factTA = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factTA"))) {
      if (op.doPCFG) {
        factTA = new TaggingEval("factor Tag", runningAverages, pd.lex);
      } else {
        // only doing dep parser, and need to get tags out in special way....
        factTA = new TaggingEval("factor Tag", runningAverages, pd.lex);
      }
    }
    AbstractEval pcfgRUO = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgRUO"))) {
      pcfgRUO = new AbstractEval.RuleErrorEval("pcfg Rule under/over");
    }
    AbstractEval pcfgCUO = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgCUO"))) {
      pcfgCUO = new AbstractEval.CatErrorEval("pcfg Category under/over");
    }
    AbstractEval pcfgCatE = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgCatE"))) {
      pcfgCatE = new EvalbByCat("pcfg Category Eval", runningAverages);
    }
    AbstractEval.ScoreEval pcfgLL = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("pcfgLL"))) {
      pcfgLL = new AbstractEval.ScoreEval("pcfgLL", runningAverages);
    }
    AbstractEval.ScoreEval depLL = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("depLL"))) {
      depLL = new AbstractEval.ScoreEval("depLL", runningAverages);
    }
    AbstractEval.ScoreEval factLL = null;
    if (Boolean.parseBoolean(Test.evals.getProperty("factLL"))) {
      factLL = new AbstractEval.ScoreEval("factLL", runningAverages);
    }
    // this one is for the various k Good/Best options.  Just for individual results
    AbstractEval kGoodLB = new Evalb("kGood LP/LR", false);

    // no annotation
    TreeAnnotatorAndBinarizer binarizerOnly;
    if (!Train.leftToRight) {
      binarizerOnly = new TreeAnnotatorAndBinarizer(tlpParams, op.forceCNF, false, false);
    } else {
      binarizerOnly = new TreeAnnotatorAndBinarizer(tlpParams.headFinder(), new LeftHeadFinder(), tlpParams, op.forceCNF, false, false);
    }

    if(Test.preTag) {
      try {
        Class[] argsClass = new Class[]{String.class};
        Object[] arguments = new Object[]{Test.taggerSerializedFile};
        System.err.printf("Loading tagger from serialized file %s ...\n",Test.taggerSerializedFile);
        tagger = (Function<List<? extends HasWord>,ArrayList<TaggedWord>>) Class.forName("edu.stanford.nlp.tagger.maxent.MaxentTagger").getConstructor(argsClass).newInstance(arguments);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    int numSkippedEvals = 0;
    boolean saidMemMessage = false;
    Timing timer = new Timing();
    for (Tree goldTree : testTreebank) {
      final ArrayList<? extends HasWord> sentence = getInputSentence(goldTree);

      timer.start();

      pwErr.println("Parsing [len. " + sentence.size() + "]: " + Sentence.listToString(sentence));
      Tree tree = null;
      try {
        if ( ! parse(sentence)) {
          pwErr.print("Sentence couldn't be parsed by grammar.");
          if (pparser != null && pparser.hasParse() && fallbackToPCFG) {
            pwErr.println("... falling back to PCFG parse.");
            tree = getBestPCFGParse(false);
          } else {
            pwErr.println();
          }
        } else {
          tree = getBestParse(false);
          if (bparser != null) pwErr.println("FactoredParser parse score is " + bparser.getBestScore());
        }

      } catch (OutOfMemoryError e) {
        if (Test.maxLength != -0xDEADBEEF) {
          // this means they explicitly asked for a length they cannot handle.
          // Throw exception.  Avoid string concatenation before throw it.
          pwErr.print("NOT ENOUGH MEMORY TO PARSE SENTENCES OF LENGTH ");
          pwErr.println(Test.maxLength);
          throw e;

        } else {
          if ( ! saidMemMessage) {
            printOutOfMemory(pwErr);
            saidMemMessage = true;
          }
          if (pparser.hasParse() && fallbackToPCFG) {
            try {
              String what = "dependency";
              if (dparser.hasParse()) {
                what = "factored";
              }
              pwErr.println("Sentence too long for " + what + " parser.  Falling back to PCFG parse...");
              tree = getBestPCFGParse(false);
            } catch (OutOfMemoryError oome) {
              oome.printStackTrace();
              pwErr.println("No memory to gather PCFG parse. Skipping...");
              pparser.nudgeDownArraySize();
            }
          } else {
            pwErr.println("Sentence has no parse using PCFG grammar (or no PCFG fallback).  Skipping...");
          }
          pwErr.println();
        }
      } catch (UnsupportedOperationException uoe) {
        pwErr.println("Sentence too long (or zero words).");
        continue;
      }

      //combo parse goes to System.out
      if (Test.verbose) {
        pwOut.println("ComboParser best");
        Tree ot = tree;
        if (ot != null && ! tlpParams.treebankLanguagePack().isStartSymbol(ot.value())) {
          ot = ot.treeFactory().newTreeNode(tlpParams.treebankLanguagePack().startSymbol(), Collections.singletonList(ot));
        }
        treePrint.printTree(ot, pwOut);
      } else {
        treePrint.printTree(tree, pwOut);
      }

      // **OUTPUT**
      // print various n-best like outputs (including 1-best)
      // print various statistics
      if (tree != null) {
        if(Test.printAllBestParses) {
          List<ScoredObject<Tree>> parses = pparser.getBestParses();
          int sz = parses.size();
          if (sz > 1) {
            pwOut.println("There were " + sz + " best PCFG parses with score " + parses.get(0).score() + '.');
            Tree transGoldTree = tc.transformTree(goldTree);
            int iii = 0;
            for (ScoredObject<Tree> sot : parses) {
              iii++;
              Tree tb = sot.object();
              Tree tbd = debinarizer.transformTree(tb);
              tbd = subcategoryStripper.transformTree(tbd);
              pwOut.println("PCFG Parse #" + iii + " with score " + tbd.score());
              tbd.pennPrint(pwOut);
              Tree tbtr = tc.transformTree(tbd);
              // pwOut.println("Tree size = " + tbtr.size() + "; depth = " + tbtr.depth());
              kGoodLB.evaluate(tbtr, transGoldTree, pwErr);
            }
          }
        }
        // Huang and Chiang (2006) Algorithm 3 output from the PCFG parser
        else if (Test.printPCFGkBest > 0 && Test.outputkBestEquivocation == null) {
          List<ScoredObject<Tree>> trees = getKBestPCFGParses(Test.printPCFGkBest);
          Tree transGoldTree = tc.transformTree(goldTree);
          int i = 0;
          for (ScoredObject<Tree> tp : trees) {
            i++;
            pwOut.println("PCFG Parse #" + i + " with score " + tp.score());
            Tree tbd = tp.object();
            tbd.pennPrint(pwOut);
            Tree tbtr = tc.transformTree(tbd);
            kGoodLB.evaluate(tbtr, transGoldTree, pwErr);
          }
        }
        // Chart parser (factored) n-best list
        else if (Test.printFactoredKGood > 0 && bparser.hasParse()) {
          // DZ: debug n best trees
          List<ScoredObject<Tree>> trees = getKGoodFactoredParses(Test.printFactoredKGood);
          Tree transGoldTree = tc.transformTree(goldTree);
          int ii = 0;
          for (ScoredObject<Tree> tp : trees) {
            ii++;
            pwOut.println("Factored Parse #" + ii + " with score " + tp.score());
            Tree tbd = tp.object();
            tbd.pennPrint(pwOut);
            Tree tbtr = tc.transformTree(tbd);
            kGoodLB.evaluate(tbtr, transGoldTree, pwOut);
          }
        }
        //1-best output
        else if(pwo != null) {
          treePrint.printTree(tree, "0", pwo);
        }

        //Print the derivational entropy
        if(Test.outputkBestEquivocation != null && Test.printPCFGkBest > 0) {
          List<ScoredObject<Tree>> trees = getKBestPCFGParses(Test.printPCFGkBest);

          double[] logScores = new double[trees.size()];
          int treeId = 0;
          for(ScoredObject<Tree> kBestTree : trees)
            logScores[treeId++] = kBestTree.score();

          //Re-normalize
          double entropy = 0.0;
          double denom = ArrayMath.logSum(logScores);
          for(int i = 0; i < logScores.length; i++) {
            double logPr = logScores[i] - denom;
            entropy += Math.exp(logPr) * (logPr / Math.log(2));
          }
          entropy *= -1; //Convert to bits
          statsPwo.printf("%f\t%d\t%d\n", entropy,trees.size(),sentence.size());
        }
      }


      // **EVALUATION**
      // Perform various evaluations specified by the user
      if (tree != null) {
        //Strip subcategories and remove punctuation for evaluation
        tree = subcategoryStripper.transformTree(tree);
        Tree treeFact = tc.transformTree(tree);

        //Setup the gold tree
        if (Test.verbose) {
          pwOut.println("Correct parse");
          treePrint.printTree(goldTree, pwOut);
        }
        Tree transGoldTree = tc.transformTree(goldTree);
        if(transGoldTree != null)
          transGoldTree = subcategoryStripper.transformTree(transGoldTree);

        //Can't do evaluation in these two cases
        if (transGoldTree == null) {
          pwErr.println("Couldn't transform gold tree for evaluation, skipping eval. Gold tree was:");
          goldTree.pennPrint(pwErr);
          numSkippedEvals++;
          continue;

        } else if(treeFact.yield().size() != transGoldTree.yield().size()) {
          pwOut.println("WARNING: Evaluation could not be performed due to guess/gold yield mismatch.");
          numSkippedEvals++;
          continue;
        }

        //PCFG eval
        Tree treePCFG = getBestPCFGParse();
        if (treePCFG != null) {
          Tree treePCFGeval = tc.transformTree(treePCFG);
          if (pcfgLB != null) {
            pcfgLB.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if(pcfgLA != null) {
            pcfgLA.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if (pcfgCB != null) {
            pcfgCB.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if (pcfgDA != null) {
            // this still doesn't work yet!  HeadFinder doesn't work on binarized tree
            Tree cwtPCFGTreeB = pparser.getBestParse();
            if(!(cwtPCFGTreeB.label() instanceof CoreLabel)) {
              cwtPCFGTreeB = cwtPCFGTreeB.deepCopy(new LabeledScoredTreeFactory(),CoreLabel.factory());
            }
            cwtPCFGTreeB.percolateHeads(tlpParams.headFinder());
            Tree goldTreeB = binarizerOnly.transformTree(goldTree);
            pcfgDA.evaluate(cwtPCFGTreeB, goldTreeB, pwErr);
          }
          if (pcfgTA != null) {
            pcfgTA.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if (pcfgLL != null && pparser != null) {
            pcfgLL.recordScore(pparser, pwErr);
          }
          if (pcfgRUO != null) {
            pcfgRUO.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if (pcfgCUO != null) {
            pcfgCUO.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
          if (pcfgCatE != null) {
            pcfgCatE.evaluate(treePCFGeval, transGoldTree, pwErr);
          }
        }

        //Dependency eval
        Tree treeDep = getBestDependencyParse();
        if (treeDep != null) {
          Tree goldTreeB = binarizerOnly.transformTree(goldTree);
          if (depDA != null) {
            depDA.evaluate(treeDep, goldTreeB, pwErr);
          }
          if (depTA != null) {
            Tree undoneTree = debinarizer.transformTree(treeDep);
            undoneTree = subcategoryStripper.transformTree(undoneTree);
            // System.err.println("subcategoryStripped tree: " + undoneTree.toStructureDebugString());
            depTA.evaluate(undoneTree, goldTree, pwErr);
          }
          if (depLL != null && dparser != null) {
            depLL.recordScore(dparser, pwErr);
          }
          Tree factTreeB;
          if (bparser != null && parseSucceeded) {
            factTreeB = bparser.getBestParse();
          } else {
            factTreeB = treeDep;
          }
          if (factDA != null) {
            factDA.evaluate(factTreeB, goldTreeB, pwErr);
          }
        }

        //Factored parser (1best) eval
        if (factLB != null) {
          factLB.evaluate(treeFact, transGoldTree, pwErr);
        }
        if(factLA != null) {
          factLA.evaluate(treeFact, transGoldTree, pwErr);
        }
        if (factTA != null) {
          factTA.evaluate(tree, br.transformTree(goldTree), pwErr);
        }
        if (factLL != null && bparser != null) {
          factLL.recordScore(bparser, pwErr);
        }
        if (factCB != null) {
          factCB.evaluate(treeFact, transGoldTree, pwErr);
        }
        if (Test.evalb) {
          // empty out scores just in case
          nanScores(tree);
          EvalbFormatWriter.writeEVALBline(treeFact, transGoldTree);
        }
      }
      pwErr.println();
    } // for tree iterator

    //Done parsing...print the results of the evaluations
    treebankTotalTtimer.done("Testing on treebank");
    if (saidMemMessage) {
      printOutOfMemory(pwErr);
    }
    if (Test.evalb) {
      EvalbFormatWriter.closeEVALBfiles();
    }
    if(numSkippedEvals != 0) {
      pwOut.printf("Unable to evaluate %d parser hypotheses due to yield mismatch\n",numSkippedEvals);
    }
    if (summary) {
      if (pcfgLB != null) pcfgLB.display(false, pwErr);
      if (pcfgLA != null) pcfgLA.display(false, pwErr);
      if (pcfgCB != null) pcfgCB.display(false, pwErr);
      if (pcfgDA != null) pcfgDA.display(false, pwErr);
      if (pcfgTA != null) pcfgTA.display(false, pwErr);
      if (pcfgLL != null && pparser != null) pcfgLL.display(false, pwErr);
      if (depDA != null) depDA.display(false, pwErr);
      if (depTA != null) depTA.display(false, pwErr);
      if (depLL != null && dparser != null) depLL.display(false, pwErr);
      if (factLB != null) factLB.display(false, pwErr);
      if (factLA != null) factLA.display(false, pwErr);
      if (factCB != null) factCB.display(false, pwErr);
      if (factDA != null) factDA.display(false, pwErr);
      if (factTA != null) factTA.display(false, pwErr);
      if (factLL != null && bparser != null) factLL.display(false, pwErr);
      if (pcfgCatE != null) pcfgCatE.display(false, pwErr);
    }
    // these ones only have a display mode, so display if turned on!!
    if (pcfgRUO != null) pcfgRUO.display(true, pwErr);
    if (pcfgCUO != null) pcfgCUO.display(true, pwErr);
    if (tsv) {
      NumberFormat nf = new DecimalFormat("0.00");
      pwErr.println("factF1\tfactDA\tfactEx\tpcfgF1\tdepDA\tfactTA\tnum");
      if (factLB != null) pwErr.print(nf.format(factLB.getEvalbF1Percent()));
      pwErr.print("\t");
      if (dparser != null && factDA != null) pwErr.print(nf.format(factDA.getEvalbF1Percent()));
      pwErr.print("\t");
      if (factLB != null) pwErr.print(nf.format(factLB.getExactPercent()));
      pwErr.print("\t");
      if (pcfgLB != null) pwErr.print(nf.format(pcfgLB.getEvalbF1Percent()));
      pwErr.print("\t");
      if (dparser != null && depDA != null) pwErr.print(nf.format(depDA.getEvalbF1Percent()));
      pwErr.print("\t");
      if (pparser != null && factTA != null) pwErr.print(nf.format(factTA.getEvalbF1Percent()));
      pwErr.print("\t");
      if (factLB != null) pwErr.print(factLB.getNum());
      pwErr.println();
    }

    double f1 = 0.0;
    if (factLB != null) {
      f1 = factLB.getEvalbF1();
    }

    //Close files (if necessary)
    if(pwo != null) pwo.close();
    if(statsPwo != null) statsPwo.close();

    return f1;
  }