/// <summary>
        /// Expected arguments are
        /// <c>-gold gold -predicted predicted</c>
        /// For example <br />
        /// <c>java edu.stanford.nlp.sentiment.ExternalEvaluate annotatedTrees.txt predictedTrees.txt</c>
        /// </summary>
        public static void Main(string[] args)
        {
            RNNOptions curOptions    = new RNNOptions();
            string     goldPath      = null;
            string     predictedPath = null;

            for (int argIndex = 0; argIndex < args.Length;)
            {
                if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-gold"))
                {
                    goldPath  = args[argIndex + 1];
                    argIndex += 2;
                }
                else
                {
                    if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-predicted"))
                    {
                        predictedPath = args[argIndex + 1];
                        argIndex     += 2;
                    }
                    else
                    {
                        int newArgIndex = curOptions.SetOption(args, argIndex);
                        if (newArgIndex == argIndex)
                        {
                            throw new ArgumentException("Unknown argument " + args[argIndex]);
                        }
                        argIndex = newArgIndex;
                    }
                }
            }
            if (goldPath == null)
            {
                log.Info("goldPath not set. Exit.");
                System.Environment.Exit(-1);
            }
            if (predictedPath == null)
            {
                log.Info("predictedPath not set. Exit.");
                System.Environment.Exit(-1);
            }
            // filterUnknown not supported because I'd need to know which sentences
            // are removed to remove them from predicted
            IList <Tree> goldTrees      = SentimentUtils.ReadTreesWithGoldLabels(goldPath);
            IList <Tree> predictedTrees = SentimentUtils.ReadTreesWithPredictedLabels(predictedPath);

            Edu.Stanford.Nlp.Sentiment.ExternalEvaluate evaluator = new Edu.Stanford.Nlp.Sentiment.ExternalEvaluate(curOptions, predictedTrees);
            evaluator.Eval(goldTrees);
            evaluator.PrintSummary();
        }
        /// <summary>Trains a sentiment model.</summary>
        /// <remarks>
        /// Trains a sentiment model.
        /// The -trainPath argument points to a labeled sentiment treebank.
        /// The trees in this data will be used to train the model parameters (also to seed the model vocabulary).
        /// The -devPath argument points to a second labeled sentiment treebank.
        /// The trees in this data will be used to periodically evaluate the performance of the model.
        /// We won't train on this data; it will only be used to test how well the model generalizes to unseen data.
        /// The -model argument specifies where to save the learned sentiment model.
        /// </remarks>
        /// <param name="args">Command line arguments</param>
        public static void Main(string[] args)
        {
            RNNOptions op               = new RNNOptions();
            string     trainPath        = "sentimentTreesDebug.txt";
            string     devPath          = null;
            bool       runGradientCheck = false;
            bool       runTraining      = false;
            bool       filterUnknown    = false;
            string     modelPath        = null;

            for (int argIndex = 0; argIndex < args.Length;)
            {
                if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-train"))
                {
                    runTraining = true;
                    argIndex++;
                }
                else
                {
                    if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-gradientcheck"))
                    {
                        runGradientCheck = true;
                        argIndex++;
                    }
                    else
                    {
                        if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-trainpath"))
                        {
                            trainPath = args[argIndex + 1];
                            argIndex += 2;
                        }
                        else
                        {
                            if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-devpath"))
                            {
                                devPath   = args[argIndex + 1];
                                argIndex += 2;
                            }
                            else
                            {
                                if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-model"))
                                {
                                    modelPath = args[argIndex + 1];
                                    argIndex += 2;
                                }
                                else
                                {
                                    if (Sharpen.Runtime.EqualsIgnoreCase(args[argIndex], "-filterUnknown"))
                                    {
                                        filterUnknown = true;
                                        argIndex++;
                                    }
                                    else
                                    {
                                        int newArgIndex = op.SetOption(args, argIndex);
                                        if (newArgIndex == argIndex)
                                        {
                                            throw new ArgumentException("Unknown argument " + args[argIndex]);
                                        }
                                        argIndex = newArgIndex;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // read in the trees
            IList <Tree> trainingTrees = SentimentUtils.ReadTreesWithGoldLabels(trainPath);

            log.Info("Read in " + trainingTrees.Count + " training trees");
            if (filterUnknown)
            {
                trainingTrees = SentimentUtils.FilterUnknownRoots(trainingTrees);
                log.Info("Filtered training trees: " + trainingTrees.Count);
            }
            IList <Tree> devTrees = null;

            if (devPath != null)
            {
                devTrees = SentimentUtils.ReadTreesWithGoldLabels(devPath);
                log.Info("Read in " + devTrees.Count + " dev trees");
                if (filterUnknown)
                {
                    devTrees = SentimentUtils.FilterUnknownRoots(devTrees);
                    log.Info("Filtered dev trees: " + devTrees.Count);
                }
            }
            // TODO: binarize the trees, then collapse the unary chains.
            // Collapsed unary chains always have the label of the top node in
            // the chain
            // Note: the sentiment training data already has this done.
            // However, when we handle trees given to us from the Stanford Parser,
            // we will have to perform this step
            // build an uninitialized SentimentModel from the binary productions
            log.Info("Sentiment model options:\n" + op);
            SentimentModel model = new SentimentModel(op, trainingTrees);

            if (op.trainOptions.initialMatrixLogPath != null)
            {
                StringUtils.PrintToFile(new File(op.trainOptions.initialMatrixLogPath), model.ToString(), false, false, "utf-8");
            }
            // TODO: need to handle unk rules somehow... at test time the tree
            // structures might have something that we never saw at training
            // time.  for example, we could put a threshold on all of the
            // rules at training time and anything that doesn't meet that
            // threshold goes into the unk.  perhaps we could also use some
            // component of the accepted training rules to build up the "unk"
            // parameter in case there are no rules that don't meet the
            // threshold
            if (runGradientCheck)
            {
                RunGradientCheck(model, trainingTrees);
            }
            if (runTraining)
            {
                Train(model, modelPath, trainingTrees, devTrees);
                model.SaveSerialized(modelPath);
            }
        }