/**
             * Push the negations all the way to just before the leaves. Also remove
             * double negatives.
             * @param root the expression to normalize
             * @return the normalized expression, which may share some or all of the
             * nodes of the original expression.
             */
            internal static ExpressionTree pushDownNot(ExpressionTree root)
            {
                if (root.getOperator() == ExpressionTree.Operator.NOT)
                {
                    ExpressionTree child = root.getChildren()[0];
                    switch (child.getOperator())
                    {
                    case ExpressionTree.Operator.NOT:
                        return(pushDownNot(child.getChildren()[0]));

                    case ExpressionTree.Operator.CONSTANT:
                        return(new ExpressionTree(child.getConstant().Value.not()));

                    case ExpressionTree.Operator.AND:
                        root = new ExpressionTree(ExpressionTree.Operator.OR);
                        foreach (ExpressionTree kid in child.getChildren())
                        {
                            root.getChildren().Add(pushDownNot(new
                                                               ExpressionTree(ExpressionTree.Operator.NOT, kid)));
                        }
                        break;

                    case ExpressionTree.Operator.OR:
                        root = new ExpressionTree(ExpressionTree.Operator.AND);
                        foreach (ExpressionTree kid in child.getChildren())
                        {
                            root.getChildren().Add(pushDownNot(new ExpressionTree
                                                                   (ExpressionTree.Operator.NOT, kid)));
                        }
                        break;

                    // for leaf, we don't do anything
                    default:
                        break;
                    }
                }
                else if (root.getChildren() != null)
                {
                    // iterate through children and push down not for each one
                    for (int i = 0; i < root.getChildren().Count; ++i)
                    {
                        root.getChildren()[i] = pushDownNot(root.getChildren()[i]);
                    }
                }
                return(root);
            }
            /**
             * Remove MAYBE values from the expression. If they are in an AND operator,
             * they are dropped. If they are in an OR operator, they kill their parent.
             * This assumes that pushDownNot has already been called.
             * @param expr The expression to clean up
             * @return The cleaned up expression
             */
            internal static ExpressionTree foldMaybe(ExpressionTree expr)
            {
                if (expr.getChildren() != null)
                {
                    for (int i = 0; i < expr.getChildren().Count; ++i)
                    {
                        ExpressionTree child = foldMaybe(expr.getChildren()[i]);
                        if (child.getConstant() == TruthValue.YES_NO_NULL)
                        {
                            switch (expr.getOperator())
                            {
                            case ExpressionTree.Operator.AND:
                                expr.getChildren().RemoveAt(i);
                                i -= 1;
                                break;

                            case ExpressionTree.Operator.OR:
                                // a maybe will kill the or condition
                                return(child);

                            default:
                                throw new InvalidOperationException("Got a maybe as child of " +
                                                                    expr);
                            }
                        }
                        else
                        {
                            expr.getChildren()[i] = child;
                        }
                    }
                    if (expr.getChildren().Count == 0)
                    {
                        return(new ExpressionTree(TruthValue.YES_NO_NULL));
                    }
                }
                return(expr);
            }