public RelationTreeNode selectRegion(string regionName, PartitionResultWrapper partition,
                                             string nonterminal)
        {
            // ASSUMPTION: strokes are already assigned to RelationTreeNodes.
            RelationTreeNode newRegion;

            switch (regionName)
            {
            case "ABOVE":
                newRegion = partition.result.ABOVE;
                break;

            case "SUPER":
                newRegion = partition.result.SUPER;
                break;

            case "SUBSC":
                newRegion = partition.result.SUBSC;
                break;

            case "BELOW":
                newRegion = partition.result.BELOW;
                break;

            case "CONTAINS":
                newRegion = partition.result.CONTAINS;
                break;

            case "TLEFT":
                newRegion = partition.result.TLEFT;
                break;

            case "BLEFT":
                newRegion = partition.result.BLEFT;
                break;

            default:
                throw new
                      ArgumentException("CreateSymbolNodes:: invalid region type detected: "
                                        + regionName);
            }
            newRegion.nodeType = nonterminal;
            return(newRegion);
        }
        public PartitionResultWrapper attachSymbol(LBT lbt, LexerResult c)
        {
            // if ( currentSymbol == null ) return new PartitionResultWrapper( null, null ); // empty

            bool final = false;             // ??

            PartitionResultWrapper p = lexer.Partition(new LBT(lbt),
                                                       currentSymbol == null ? null : currentSymbol.symbol.segment,
                                                       c == null ? null : c.segment,
                                                       currentSymbol == null ? PartitionClass.Regular : getPartitionClass(currentSymbol.symbol.segment.classification[0].note),
                                                       getPartitionClass(c == null ? null : c.segment.classification[0].note),
                                                       currentSymbol == null ? null : currentSymbol.symbol.TLEFT,
                                                       currentSymbol == null ? null : currentSymbol.symbol.BLEFT);

            // if the symbol already has regions assigned, they need to be removed
            if (currentSymbol != null /* && !p.result.empty() */)
            {
                List <ParseTreeNode> n_children_tmp = new List <ParseTreeNode>(currentSymbol.parent.children);
                for (int i = 0; i < n_children_tmp.Count; i++)
                {
                    if (n_children_tmp[i] is RelationTreeNode)
                    {
                        currentSymbol.parent.children.Remove(n_children_tmp[i]);
                    }
                }
            }

            // if not a legal partition or leftover strokes, backtrack
            if (!legalPartition(p, currentSymbol) || (final && unusedStrokes > 0))
            {
                return(null);
            }

            // otherwise, assign new regions to the current symbol
            if (p.result.ABOVE.lbt.strokes.Count != 0)
            {
                currentSymbol.parent.children.Add(p.result.ABOVE);
                unusedStrokes -= p.result.ABOVE.lbt.strokes.Count;
            }

            if (p.result.BELOW.lbt.strokes.Count != 0)
            {
                currentSymbol.parent.children.Add(p.result.BELOW);
                unusedStrokes -= p.result.BELOW.lbt.strokes.Count;
            }

            if (p.result.CONTAINS.lbt.strokes.Count != 0)
            {
                currentSymbol.parent.children.Add(p.result.CONTAINS);
                unusedStrokes -= p.result.CONTAINS.lbt.strokes.Count;
            }

            if (p.result.SUBSC.lbt.strokes.Count != 0)
            {
                currentSymbol.parent.children.Add(p.result.SUBSC);
                unusedStrokes -= p.result.SUBSC.lbt.strokes.Count;
            }

            if (p.result.SUPER.lbt.strokes.Count != 0)
            {
                currentSymbol.parent.children.Add(p.result.SUPER);
                unusedStrokes -= p.result.SUPER.lbt.strokes.Count;
            }

            // add BLEFT/TLEFT to "right" symbol
            // actual attachment delayed until the new PreviousSymbol object is created
            if (p.result.BLEFT.lbt.strokes.Count != 0)
            {
                if (c == null)
                {
                    currentSymbol.parent.children.Add(p.result.BLEFT);
                    unusedStrokes -= p.result.BLEFT.lbt.strokes.Count;
                }
                else
                {
                    c.BLEFT = p.result.BLEFT;
                }
            }
            if (p.result.TLEFT.lbt.strokes.Count != 0)
            {
                if (c == null)
                {
                    currentSymbol.parent.children.Add(p.result.TLEFT);
                    unusedStrokes -= p.result.TLEFT.lbt.strokes.Count;
                }
                else
                {
                    c.TLEFT = p.result.TLEFT;
                }
            }

            // update LBT; should this be done elsewhere???
            if (c != null)
            {
                foreach (Stroke s in c.segment.strokes)
                {
                    p.lbt.PruneNode(s, LBT.DefaultAdjacentCriterion);
                }
                c.lbt = p.lbt;
                //} else {
                //currentSymbol.symbol.lbt = p.lbt;
            }

            //unusedStrokes = p.lbt.strokes.Count; // ????????

            return(p);
        }
        // END ADDED JULY 7, 12

        public bool legalPartition(PartitionResultWrapper presult, PreviousSymbol p)
        {
            if (p == null)
            {
                return(true);                         // ???
            }
            // Compile non-empty regions.
            List <string> nonEmptyRegions = new List <string>();

            if (presult.result.ABOVE != null &&             //presult.result.ABOVE.strokes != null &&
                presult.result.ABOVE.strokes.Count > 0)
            {
                nonEmptyRegions.Add("ABOVE");
            }
            if (presult.result.BELOW != null &&            // presult.result.BELOW.strokes != null &&
                presult.result.BELOW.strokes.Count > 0)
            {
                nonEmptyRegions.Add("BELOW");
            }
            if (presult.result.SUPER != null &&             //presult.result.SUPER.strokes != null &&
                presult.result.SUPER.strokes.Count > 0)
            {
                nonEmptyRegions.Add("SUPER");
            }
            if (presult.result.SUBSC != null &&             //presult.result.SUBSC.strokes != null &&
                presult.result.SUBSC.strokes.Count > 0)
            {
                nonEmptyRegions.Add("SUBSC");
            }
            if (presult.result.CONTAINS != null &&            // presult.result.CONTAINS.strokes != null &&
                presult.result.CONTAINS.strokes.Count > 0)
            {
                nonEmptyRegions.Add("CONTAINS");
            }

            // account for regions currently assigned
            foreach (string reg in p.regions)
            {
                if (!nonEmptyRegions.Contains(reg))
                {
                    nonEmptyRegions.Add(reg);
                }
            }

            // TODO: LAYOUT CLASS?
#if DEBUG
            Console.WriteLine("[legalPartition] p.parent.nodeType = <{0}>", p.parent.nodeType);
#endif

            // get layout (regions) for symbol as specified by the grammar
            // FIRST, try looking up parent NT directly as the class
            // IF THAT FAILS, look up the class(es) based on the symbol

            List <string[]> grammarLayout;
            try {
                grammarLayout = grammar.GetRegionLayouts(p.parent.nodeType);
            } catch (Exception) {
                try {
                    grammarLayout = grammar.GetRegionLayouts(p.symbol.segment.classification[0].symbol);
                } catch (Exception) {
                    return(false);                    // no, just no
                }
            }

            foreach (string[] regions in grammarLayout)
            {
                int  matchedRegions = 0;
                bool satisfies      = true;
                for (int i = 0; i < regions.Length; i += 2)
                {
                    bool   optional = regions[i][0] == '@';                       // ow, assume required
                    string region   = regions[i].Substring(1);
                    // string nt = regions[ i + 1 ];
                    if (nonEmptyRegions.Contains(region))
                    {
                        matchedRegions++;
                    }
                    else if (!optional)
                    {
                        satisfies = false;
                    }
                }
                if (satisfies && matchedRegions == nonEmptyRegions.Count)
                {
                    // add starting NTs to the regions
                    for (int i = 0; i < regions.Length; i += 2)
                    {
                        string region = regions[i].Substring(1);
                        selectRegion(region, presult, regions[i + 1]);
                    }

                    return(true);                    // at least one satisfies
                }
            }

            return(false);
        }
        public void applyRules(List <ParseTreeNode> arg_nlist)
        {
#if DEBUG
            Console.WriteLine("[applyRules] entered.");
            treeRoot.ShowTree(4, null);
            Console.WriteLine("Min Required Strokes: " + minRequiredStrokes);
            Console.WriteLine("Call: " + apply_rule_counter);
#else
            //Console.Write(apply_rule_counter);
            //Console.Write('\r');
#endif
            // increment counter
            apply_rule_counter++;
            if (arg_nlist.Count == 0)
            {
                return;
            }
            if (unusedStrokes < 1 && arg_nlist[0] != ParseTreeNode.EndOfBaseline && !(arg_nlist[0] is RelationTreeNode))
            {
                return;
            }

            // if ( unusedStrokes > 0 && !( arg_nlist[ 0 ].GetType().Equals( typeof( ParseTreeNode ) ) ) ) return;

            List <ParseTreeNode> nlist = new List <ParseTreeNode>(arg_nlist);
            ParseTreeNode        n     = nlist[0];
            nlist.RemoveAt(0);

            if (n == ParseTreeNode.EndOfBaseline)
            {
                // END-OF-BASELINE CASE


                PartitionResultWrapper pr = attachSymbol(currentSymbol.symbol.lbt, null);

                // continue only if valid partition made
                if (pr != null)
                {
                    if (nlist.Count == 0 && unusedStrokes == 0 && unusedInputStrokes == 0)
                    {
#if DEBUG
                        Console.WriteLine("***ACCEPT***");
#endif
                        acceptCurrentParseTree();
                    }

                    int nodes_added = nlist.Count;

                    if (pr.result != null && pr.result.ABOVE.lbt.strokes.Count != 0)
                    {
                        nlist.Add(pr.result.ABOVE);
                    }
                    if (pr.result != null && pr.result.BELOW.lbt.strokes.Count != 0)
                    {
                        nlist.Add(pr.result.BELOW);
                    }
                    if (pr.result != null && pr.result.CONTAINS.lbt.strokes.Count != 0)
                    {
                        nlist.Add(pr.result.CONTAINS);
                    }
                    if (pr.result != null && pr.result.SUBSC.lbt.strokes.Count != 0)
                    {
                        nlist.Add(pr.result.SUBSC);
                    }
                    if (pr.result != null && pr.result.SUPER.lbt.strokes.Count != 0)
                    {
                        nlist.Add(pr.result.SUPER);
                    }

                    // add BLEFT/TLEFT
                    //if ( pr.result != null && pr.result.BLEFT.lbt.strokes.Count != 0 ) nlist.Add( pr.result.BLEFT );
                    //if ( pr.result != null && pr.result.TLEFT.lbt.strokes.Count != 0 ) nlist.Add( pr.result.TLEFT );

                    nodes_added         = nlist.Count - nodes_added;
                    minRequiredStrokes += nodes_added;

                    applyRules(nlist);
                }
                else
                {
#if DEBUG
                    Console.WriteLine("**BACKTRACK: end-of-baseline, invalid partition");
#endif
                }
            }
            else if (n is RelationTreeNode)
            {
                // handle relation nodes by adding new parse tree node
                RelationTreeNode rtn = n as RelationTreeNode;
                ParseTreeNode    ptn = new ParseTreeNode();
                ptn.strokes  = rtn.strokes;
                ptn.nodeType = rtn.nodeType;
                // increment here for new production
                ptn.lbt = rtn.lbt;
                rtn.children.Clear();                 // remove all before
                rtn.children.Add(ptn);
                parse(ptn.lbt, ptn, nlist);
            }
            else if (n.nodeType.StartsWith("*"))
            {             // if n generates terminal symbols
                List <LexerResult> C = SelectCandidateSymbols(n.nodeType);
                //candidateSymbols = C;
                foreach (LexerResult c in C)
                {
                    PartitionResultWrapper pr = attachSymbol(currentSymbol == null ? initLBT : currentSymbol.symbol.lbt, c);
                    if (pr != null)
                    {
                        pushCurrentState();

                        // update current state
                        currentSymbol       = new PreviousSymbol(c, n, null, null, true);
                        unusedStrokes      -= currentSymbol.symbol.segment.strokes.Count;
                        unusedInputStrokes -= currentSymbol.symbol.segment.strokes.Count;
                        // remove one of the min required strokes for the current token
                        minRequiredStrokes -= 1;

                        // prune by  number of strokes left
                        if (unusedInputStrokes < minRequiredStrokes)
                        {
                            popCurrentState();
                            return;                             // continue;
                        }

                        List <string> layoutClasses = grammar.GetLayoutClassesFromTerminal(c.segment.classification[0].symbol);

                        if (layoutClasses.Count == 0)
                        {
                            continue;
                        }

                        candidateSymbols = new List <LexerResult>();
                        foreach (string layoutClass in layoutClasses)
                        {
                            List <LexerResult> res = lexer.Next(c.lbt, c.segment, layoutClass, MAX_NEIGHBORS);
                            foreach (LexerResult r in res)
                            {
                                if (!candidateSymbols.Contains(r))
                                {
                                    candidateSymbols.Add(r);
                                }
                            }
                        }


                        if (candidateSymbols.Count == 0 && !nlist.Contains(ParseTreeNode.EndOfBaseline))
                        {
                            nlist.Insert(0, ParseTreeNode.EndOfBaseline);
                        }
                        // not end of baseline, so prune and backtrack if necessary
                        else if (nlist.Count > 0 && (nlist[0] is RelationTreeNode == false))
                        {
                            // prune candidates based on the current node type
                            foreach (LexerResult lr in candidateSymbols)
                            {
                                for (int k = 0; k < lr.segment.classification.Count; k++)
                                {
                                    if (grammar.NonTerminalCanGenerateTerminal(nlist[0].nodeType, lr.segment.classification[k].symbol) == false)
                                    {
                                        lr.segment.classification.RemoveAt(k--);
                                    }
                                }
                            }
                            // remove candidate symbols which contain no symbol alternatives
                            for (int k = 0; k < candidateSymbols.Count; k++)
                            {
                                if (candidateSymbols[k].segment.classification.Count == 0)
                                {
                                    candidateSymbols.RemoveAt(k--);
                                }
                            }

                            // no valid symbols given the grammar, so bbreak out early
                            if (candidateSymbols.Count == 0)
                            {
                                popCurrentState();
                                return;                                 // continue;
                            }
                        }

                        SymbolTreeNode nc = new SymbolTreeNode(c);
                        n.children.Add(nc);

                        /*
                         * if ( nlist.Count == 0 && unusedStrokes == 0 ) {
                         *      if ( unusedInputStrokes == 0 ) acceptCurrentParseTree();
                         * } else {
                         */
                        // append relation nodes to the END

                        List <RelationTreeNode> nlist_rels = new List <RelationTreeNode>();
                        if (pr.result != null && pr.result.ABOVE.lbt.strokes.Count != 0)
                        {
                            nlist_rels.Add(pr.result.ABOVE);
                        }
                        if (pr.result != null && pr.result.BELOW.lbt.strokes.Count != 0)
                        {
                            nlist_rels.Add(pr.result.BELOW);
                        }
                        if (pr.result != null && pr.result.CONTAINS.lbt.strokes.Count != 0)
                        {
                            nlist_rels.Add(pr.result.CONTAINS);
                        }
                        if (pr.result != null && pr.result.SUBSC.lbt.strokes.Count != 0)
                        {
                            nlist_rels.Add(pr.result.SUBSC);
                        }
                        if (pr.result != null && pr.result.SUPER.lbt.strokes.Count != 0)
                        {
                            nlist_rels.Add(pr.result.SUPER);
                        }
                        //if ( pr.result != null && pr.result.BLEFT.lbt.strokes.Count != 0 ) nlist_rels.Add( pr.result.BLEFT );
                        //if ( pr.result != null && pr.result.TLEFT.lbt.strokes.Count != 0 ) nlist_rels.Add( pr.result.TLEFT );
                        foreach (RelationTreeNode rtn in nlist_rels)
                        {
                            nlist.Add(rtn);
                        }

                        minRequiredStrokes += nlist_rels.Count;

                        applyRules(nlist);
                        //}

                        if (nlist.Count > 0 && nlist[0] == ParseTreeNode.EndOfBaseline)
                        {
                            nlist.RemoveAt(0);                                                                                           // !
                        }
                        // remove any leftover relation nodes
                        n.children.Remove(nc);
                        List <ParseTreeNode> n_children_tmp = new List <ParseTreeNode>(n.children);
                        for (int i = 0; i < n_children_tmp.Count; i++)
                        {
                            if (n_children_tmp[i] is RelationTreeNode)
                            {
                                n.children.Remove(n_children_tmp[i]);
                            }
                        }

                        foreach (RelationTreeNode rtn in nlist_rels)
                        {
                            nlist.Remove(rtn);
                        }
                        popCurrentState();
                    }
                }
            }
            else
            {
                // NONTERMINALS
                //n.lexResult.segment.classification[0].symbol;
                List <string[]> productions = grammar.GetProductions(n.nodeType);
                if (productions == null)
                {
#if DEBUG
                    Console.Error.WriteLine("Error: invalid nonterminal ({0}).", n.nodeType);
#endif
                    return;
                }

                // remove one for the token we are replacing with productions
                minRequiredStrokes--;

                foreach (string[] production in productions)
                {
                    minRequiredStrokes += production.Length;

                    // prune by  number of strokes left
                    if (unusedInputStrokes < minRequiredStrokes)
                    {
                        minRequiredStrokes -= production.Length;
                        continue;
                    }

                    // prune based on the candidate symbols and the first rule in production
                    bool candidate_can_be_generated = false;
                    foreach (LexerResult lr in candidateSymbols)
                    {
                        foreach (Classification csf in lr.segment.classification)
                        {
                            if (grammar.NonTerminalCanGenerateTerminal(production[0], csf.symbol))
                            {
                                candidate_can_be_generated = true;
                                break;
                            }
                        }
                        if (candidate_can_be_generated)
                        {
                            break;
                        }
                    }

                    if (!candidate_can_be_generated)
                    {
                        minRequiredStrokes -= production.Length;
                        continue;
                    }


                    List <ParseTreeNode> nodes = new List <ParseTreeNode>();
                    foreach (string p in production)
                    {
                        ParseTreeNode n0 = new ParseTreeNode();
                        n0.nodeType  = p;
                        n0.lexResult = null;
                        nodes.Add(n0);
                        n.children.Add(n0);
                    }

                    for (int i = nodes.Count - 1; i >= 0; i--)
                    {
                        nlist.Insert(0, nodes[i]);
                    }
                    applyRules(nlist);

                    // restore min required strokes
                    minRequiredStrokes -= production.Length;

                    foreach (ParseTreeNode node in nodes)
                    {
                        n.children.Remove(node);
                        nlist.Remove(node);
                    }
                }
                minRequiredStrokes++;
            }
        }