/// <summary>
        /// Creates cons features involving the 3 specified nodes and adds them to the specified feature list.
        /// </summary>
        /// <param name="features">The list of features.</param>
        /// <param name="c0">The first node.</param>
        /// <param name="c1">The second node.</param>
        /// <param name="c2">The third node.</param>
        /// <param name="punct1s">The punctuation between the first and second node.</param>
        /// <param name="punct2s">The punctuation between the second and third node.</param>
        /// <param name="trigram">Specifies whether lexical tri-gram features between these nodes should be generated.</param>
        /// <param name="bigram1">Specifies whether lexical bi-gram features between the first and second node should be generated.</param>
        /// <param name="bigram2">Specifies whether lexical bi-gram features between the second and third node should be generated.</param>
        protected void Cons3(List <string> features, Cons c0, Cons c1, Cons c2, SortedSet <Parse> punct1s,
                             SortedSet <Parse> punct2s, bool trigram, bool bigram1, bool bigram2)
        {
            //  features.add("stage=cons(0),cons(1),cons(2)");
            if (punct1s != null)
            {
                if (c0.index == -2)
                {
                    foreach (var p in punct1s)
                    {
                        var punctbo = PunctBo(p, c1.index <= 0 ? c1.index - 1 : c1.index);
                        //punct(-2)
                        //TODO consider changing
                        //features.add(punct);

                        //punctbo(-2)
                        features.Add(punctbo);
                    }
                }
            }
            if (punct2s != null)
            {
                if (c2.index == 2)
                {
                    foreach (var p in punct2s)
                    {
                        //          String punct = punct(p,c2.index);
                        var punctbo = PunctBo(p, c2.index <= 0 ? c2.index - 1 : c2.index);
                        //punct(2)
                        //TODO consider changing
                        //features.add(punct);

                        //punctbo(2)
                        features.Add(punctbo);
                    }
                }
                if (punct1s != null)
                {
                    //cons(0),punctbo(1),cons(1),punctbo(2),cons(2)
                    foreach (var pi2 in punct2s)
                    {
                        var punctbo2 = PunctBo(pi2, c2.index <= 0 ? c2.index - 1 : c2.index);

                        foreach (var pi1 in punct1s)
                        {
                            var punctbo1 = PunctBo(pi1, c1.index <= 0 ? c1.index - 1 : c1.index);
                            if (trigram)
                            {
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                            }

                            if (bigram2)
                            {
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                            }
                            if (c0.unigram && c2.unigram)
                            {
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);
                            }
                            if (bigram1)
                            {
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);
                            }

                            if (c2.unigram)
                            {
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," +
                                             c2.cons);
                            }
                            if (c1.unigram)
                            {
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," +
                                             c2.consbo);
                            }
                            if (c0.unigram)
                            {
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," +
                                             c2.consbo);
                            }

                            features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);
                            if (zeroBackOff)
                            {
                                if (bigram1)
                                {
                                    features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2);
                                }
                                if (c1.unigram)
                                {
                                    features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2);
                                }
                                if (c0.unigram)
                                {
                                    features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2);
                                }
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2);
                            }
                        }
                    }
                }
                else
                {
                    //punct1s == null

                    foreach (var pi2 in punct2s)
                    {
                        var punctbo2 = PunctBo(pi2, c2.index <= 0 ? c2.index - 1 : c2.index);
                        if (trigram)
                        {
                            features.Add(c0.cons + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                        }

                        if (bigram2)
                        {
                            features.Add(c0.consbo + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                        }
                        if (c0.unigram && c2.unigram)
                        {
                            features.Add(c0.cons + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);
                        }

                        if (bigram1)
                        {
                            features.Add(c0.cons + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);
                        }

                        if (c2.unigram)
                        {
                            features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);
                        }
                        if (c1.unigram)
                        {
                            features.Add(c0.consbo + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);
                        }
                        if (c0.unigram)
                        {
                            features.Add(c0.cons + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);
                        }

                        features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);

                        if (zeroBackOff)
                        {
                            if (bigram1)
                            {
                                features.Add(c0.cons + "," + c1.cons + "," + punctbo2);
                            }
                            if (c1.unigram)
                            {
                                features.Add(c0.consbo + "," + c1.cons + "," + punctbo2);
                            }
                            if (c0.unigram)
                            {
                                features.Add(c0.cons + "," + c1.consbo + "," + punctbo2);
                            }
                            features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2);
                        }
                    }
                }
            }
            else
            {
                if (punct1s != null)
                {
                    //cons(0),punctbo(1),cons(1),cons(2)
                    foreach (var pi1 in punct1s)
                    {
                        var punctbo1 = PunctBo(pi1, c1.index <= 0 ? c1.index - 1 : c1.index);
                        if (trigram)
                        {
                            features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + c2.cons);
                        }

                        if (bigram2)
                        {
                            features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + c2.cons);
                        }
                        if (c0.unigram && c2.unigram)
                        {
                            features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + c2.cons);
                        }
                        if (bigram1)
                        {
                            features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + c2.consbo);
                        }

                        if (c2.unigram)
                        {
                            features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + c2.cons);
                        }
                        if (c1.unigram)
                        {
                            features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + c2.consbo);
                        }
                        if (c0.unigram)
                        {
                            features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + c2.consbo);
                        }

                        features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + c2.consbo);

                        //zero backoff case covered by cons(0)cons(1)
                    }
                }
                else
                {
                    //cons(0),cons(1),cons(2)
                    if (trigram)
                    {
                        features.Add(c0.cons + "," + c1.cons + "," + c2.cons);
                    }

                    if (bigram2)
                    {
                        features.Add(c0.consbo + "," + c1.cons + "," + c2.cons);
                    }
                    if (c0.unigram && c2.unigram)
                    {
                        features.Add(c0.cons + "," + c1.consbo + "," + c2.cons);
                    }
                    if (bigram1)
                    {
                        features.Add(c0.cons + "," + c1.cons + "," + c2.consbo);
                    }

                    if (c2.unigram)
                    {
                        features.Add(c0.consbo + "," + c1.consbo + "," + c2.cons);
                    }
                    if (c1.unigram)
                    {
                        features.Add(c0.consbo + "," + c1.cons + "," + c2.consbo);
                    }
                    if (c0.unigram)
                    {
                        features.Add(c0.cons + "," + c1.consbo + "," + c2.consbo);
                    }

                    features.Add(c0.consbo + "," + c1.consbo + "," + c2.consbo);
                }
            }
        }
        /// <summary>
        /// Creates cons features involving the 3 specified nodes and adds them to the specified feature list.
        /// </summary>
        /// <param name="features">The list of features.</param>
        /// <param name="c0">The first node.</param>
        /// <param name="c1">The second node.</param>
        /// <param name="c2">The third node.</param>
        /// <param name="punct1s">The punctuation between the first and second node.</param>
        /// <param name="punct2s">The punctuation between the second and third node.</param>
        /// <param name="trigram">Specifies whether lexical tri-gram features between these nodes should be generated.</param>
        /// <param name="bigram1">Specifies whether lexical bi-gram features between the first and second node should be generated.</param>
        /// <param name="bigram2">Specifies whether lexical bi-gram features between the second and third node should be generated.</param>
        protected void Cons3(List<string> features, Cons c0, Cons c1, Cons c2, SortedSet<Parse> punct1s,
            SortedSet<Parse> punct2s, bool trigram, bool bigram1, bool bigram2) {
            //  features.add("stage=cons(0),cons(1),cons(2)");
            if (punct1s != null) {
                if (c0.index == -2) {
                    foreach (var p in punct1s) {
                        var punctbo = PunctBo(p, c1.index <= 0 ? c1.index - 1 : c1.index);
                        //punct(-2)
                        //TODO consider changing
                        //features.add(punct);

                        //punctbo(-2)
                        features.Add(punctbo);
                    }
                }
            }
            if (punct2s != null) {
                if (c2.index == 2) {
                    foreach (var p in punct2s) {
                        //          String punct = punct(p,c2.index);
                        var punctbo = PunctBo(p, c2.index <= 0 ? c2.index - 1 : c2.index);
                        //punct(2)
                        //TODO consider changing
                        //features.add(punct);

                        //punctbo(2)
                        features.Add(punctbo);
                    }
                }
                if (punct1s != null) {
                    //cons(0),punctbo(1),cons(1),punctbo(2),cons(2)
                    foreach (var pi2 in punct2s) {
                        var punctbo2 = PunctBo(pi2, c2.index <= 0 ? c2.index - 1 : c2.index);

                        foreach (var pi1 in punct1s) {
                            var punctbo1 = PunctBo(pi1, c1.index <= 0 ? c1.index - 1 : c1.index);
                            if (trigram)
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.cons);

                            if (bigram2)
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                            if (c0.unigram && c2.unigram)
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);
                            if (bigram1)
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);

                            if (c2.unigram)
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," +
                                             c2.cons);
                            if (c1.unigram)
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2 + "," +
                                             c2.consbo);
                            if (c0.unigram)
                                features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," +
                                             c2.consbo);

                            features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);
                            if (zeroBackOff) {
                                if (bigram1) features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + punctbo2);
                                if (c1.unigram)
                                    features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + punctbo2);
                                if (c0.unigram)
                                    features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + punctbo2);
                                features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + punctbo2);
                            }
                        }
                    }
                } else {
                    //punct1s == null

                    foreach (var pi2 in punct2s) {
                        var punctbo2 = PunctBo(pi2, c2.index <= 0 ? c2.index - 1 : c2.index);
                        if (trigram) features.Add(c0.cons + "," + c1.cons + "," + punctbo2 + "," + c2.cons);

                        if (bigram2) features.Add(c0.consbo + "," + c1.cons + "," + punctbo2 + "," + c2.cons);
                        if (c0.unigram && c2.unigram)
                            features.Add(c0.cons + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);

                        if (bigram1) features.Add(c0.cons + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);

                        if (c2.unigram) features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2 + "," + c2.cons);
                        if (c1.unigram) features.Add(c0.consbo + "," + c1.cons + "," + punctbo2 + "," + c2.consbo);
                        if (c0.unigram) features.Add(c0.cons + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);

                        features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2 + "," + c2.consbo);

                        if (zeroBackOff) {
                            if (bigram1) features.Add(c0.cons + "," + c1.cons + "," + punctbo2);
                            if (c1.unigram) features.Add(c0.consbo + "," + c1.cons + "," + punctbo2);
                            if (c0.unigram) features.Add(c0.cons + "," + c1.consbo + "," + punctbo2);
                            features.Add(c0.consbo + "," + c1.consbo + "," + punctbo2);
                        }
                    }
                }
            } else {
                if (punct1s != null) {
                    //cons(0),punctbo(1),cons(1),cons(2)
                    foreach (var pi1 in punct1s) {
                        var punctbo1 = PunctBo(pi1, c1.index <= 0 ? c1.index - 1 : c1.index);
                        if (trigram) features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + c2.cons);

                        if (bigram2) features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + c2.cons);
                        if (c0.unigram && c2.unigram)
                            features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + c2.cons);
                        if (bigram1) features.Add(c0.cons + "," + punctbo1 + "," + c1.cons + "," + c2.consbo);

                        if (c2.unigram) features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + c2.cons);
                        if (c1.unigram) features.Add(c0.consbo + "," + punctbo1 + "," + c1.cons + "," + c2.consbo);
                        if (c0.unigram) features.Add(c0.cons + "," + punctbo1 + "," + c1.consbo + "," + c2.consbo);

                        features.Add(c0.consbo + "," + punctbo1 + "," + c1.consbo + "," + c2.consbo);

                        //zero backoff case covered by cons(0)cons(1)
                    }
                } else {
                    //cons(0),cons(1),cons(2)
                    if (trigram) features.Add(c0.cons + "," + c1.cons + "," + c2.cons);

                    if (bigram2) features.Add(c0.consbo + "," + c1.cons + "," + c2.cons);
                    if (c0.unigram && c2.unigram) features.Add(c0.cons + "," + c1.consbo + "," + c2.cons);
                    if (bigram1) features.Add(c0.cons + "," + c1.cons + "," + c2.consbo);

                    if (c2.unigram) features.Add(c0.consbo + "," + c1.consbo + "," + c2.cons);
                    if (c1.unigram) features.Add(c0.consbo + "," + c1.cons + "," + c2.consbo);
                    if (c0.unigram) features.Add(c0.cons + "," + c1.consbo + "," + c2.consbo);

                    features.Add(c0.consbo + "," + c1.consbo + "," + c2.consbo);
                }
            }
        }
        protected void Cons2(List <string> features, Cons c0, Cons c1, SortedSet <Parse> punct1s, bool bigram)
        {
            if (punct1s != null)
            {
                foreach (var p in punct1s)
                {
                    var punctbo = PunctBo(p, c1.index <= 0 ? c1.index - 1 : c1.index);

                    //punctbo(1);
                    features.Add(punctbo);
                    if (c0.index == 0)
                    {
                        //TODO look at removing case
                        //cons(0)punctbo(1)
                        if (c0.unigram)
                        {
                            features.Add(c0.cons + "," + punctbo);
                        }
                        features.Add(c0.consbo + "," + punctbo);
                    }
                    if (c1.index == 0)
                    {
                        //TODO look at removing case
                        //punctbo(1)cons(1)
                        if (c1.unigram)
                        {
                            features.Add(punctbo + "," + c1.cons);
                        }
                        features.Add(punctbo + "," + c1.consbo);
                    }

                    //cons(0)punctbo(1)cons(1)
                    if (bigram)
                    {
                        features.Add(c0.cons + "," + punctbo + "," + c1.cons);
                    }
                    if (c1.unigram)
                    {
                        features.Add(c0.consbo + "," + punctbo + "," + c1.cons);
                    }
                    if (c0.unigram)
                    {
                        features.Add(c0.cons + "," + punctbo + "," + c1.consbo);
                    }
                    features.Add(c0.consbo + "," + punctbo + "," + c1.consbo);
                }
            }
            else
            {
                //cons(0),cons(1)
                if (bigram)
                {
                    features.Add(c0.cons + "," + c1.cons);
                }
                if (c1.unigram)
                {
                    features.Add(c0.consbo + "," + c1.cons);
                }
                if (c0.unigram)
                {
                    features.Add(c0.cons + "," + c1.consbo);
                }
                features.Add(c0.consbo + "," + c1.consbo);
            }
        }
        protected void Cons2(List<string> features, Cons c0, Cons c1, SortedSet<Parse> punct1s, bool bigram) {
            if (punct1s != null) {
                foreach (var p in punct1s) {
                    var punctbo = PunctBo(p, c1.index <= 0 ? c1.index - 1 : c1.index);

                    //punctbo(1);
                    features.Add(punctbo);
                    if (c0.index == 0) {
                        //TODO look at removing case
                        //cons(0)punctbo(1)
                        if (c0.unigram) features.Add(c0.cons + "," + punctbo);
                        features.Add(c0.consbo + "," + punctbo);
                    }
                    if (c1.index == 0) {
                        //TODO look at removing case
                        //punctbo(1)cons(1)
                        if (c1.unigram) features.Add(punctbo + "," + c1.cons);
                        features.Add(punctbo + "," + c1.consbo);
                    }

                    //cons(0)punctbo(1)cons(1)
                    if (bigram) features.Add(c0.cons + "," + punctbo + "," + c1.cons);
                    if (c1.unigram) features.Add(c0.consbo + "," + punctbo + "," + c1.cons);
                    if (c0.unigram) features.Add(c0.cons + "," + punctbo + "," + c1.consbo);
                    features.Add(c0.consbo + "," + punctbo + "," + c1.consbo);
                }
            } else {
                //cons(0),cons(1)
                if (bigram) features.Add(c0.cons + "," + c1.cons);
                if (c1.unigram) features.Add(c0.consbo + "," + c1.cons);
                if (c0.unigram) features.Add(c0.cons + "," + c1.consbo);
                features.Add(c0.consbo + "," + c1.consbo);
            }
        }