public virtual Expr Le(Expr lhs, Expr rhs)
 {
     return(UB.Le(lhs, rhs));
 }
        public override Expr VisitNAryExpr(NAryExpr node)
        {
            // Need to duplicate arguments first (doing post order traversal)
            var newArgs = new List <Expr>();

            foreach (var oldExpr in node.Args)
            {
                var newExpr = (Expr)this.Visit(oldExpr);
                newArgs.Add(newExpr);
            }

            // Now can finally duplicate this node now we've done our children

            if (node.Fun is FunctionCall)
            {
                var    FC        = (FunctionCall)node.Fun;
                string bvbuiltin = QKeyValue.FindStringAttribute(FC.Func.Attributes, "bvbuiltin");
                if (bvbuiltin != null)
                {
                    return(HandleBvBuiltIns(FC, bvbuiltin, newArgs));
                }
                else
                {
                    string builtin = QKeyValue.FindStringAttribute(FC.Func.Attributes, "builtin");
                    if (builtin != null)
                    {
                        return(HandleBuiltIns(FC, builtin, newArgs));
                    }
                }
                // Not a builtin so treat as uninterpreted function call
                return(Builder.UFC(FC, newArgs.ToArray()));
            }
            else if (node.Fun is UnaryOperator)
            {
                var U = (UnaryOperator)node.Fun;
                Debug.Assert(newArgs.Count == 1);
                switch (U.Op)
                {
                case UnaryOperator.Opcode.Neg:
                    return(Builder.Neg(newArgs[0]));

                case UnaryOperator.Opcode.Not:
                    return(Builder.Not(newArgs[0]));

                default:
                    throw new NotImplementedException("Unary operator not supported");
                }
            }
            else if (node.Fun is BinaryOperator)
            {
                var B = (BinaryOperator)node.Fun;
                Debug.Assert(newArgs.Count == 2);
                switch (B.Op)
                {
                // Integer or Real number operators
                case BinaryOperator.Opcode.Add:
                    return(Builder.Add(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Sub:
                    return(Builder.Sub(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Mul:
                    return(Builder.Mul(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Div:
                    return(Builder.Div(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Mod:
                    return(Builder.Mod(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.RealDiv:
                    return(Builder.RealDiv(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Pow:
                    return(Builder.Pow(newArgs[0], newArgs[1]));

                // Comparision operators
                case BinaryOperator.Opcode.Eq:
                    return(Builder.Eq(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Neq:
                    return(Builder.NotEq(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Gt:
                    return(Builder.Gt(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Ge:
                    return(Builder.Ge(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Lt:
                    return(Builder.Lt(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Le:
                    return(Builder.Le(newArgs[0], newArgs[1]));

                // Bool operators
                case BinaryOperator.Opcode.And:
                    return(Builder.And(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Or:
                    return(Builder.Or(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Imp:
                    return(Builder.Imp(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Iff:
                    return(Builder.Iff(newArgs[0], newArgs[1]));

                case BinaryOperator.Opcode.Subtype:
                    throw new NotImplementedException("SubType binary operator support not implemented");

                default:
                    throw new NotSupportedException("Binary operator" + B.Op.ToString() + "not supported!");
                }
            }
            else if (node.Fun is MapStore)
            {
                Debug.Assert(newArgs.Count >= 3);
                // FIXME: We're probably making too many lists/arrays here
                var map     = newArgs[0];
                var value   = newArgs[newArgs.Count - 1]; // Last argument is value to store
                var indices = new List <Expr>();
                for (int index = 1; index < newArgs.Count - 1; ++index)
                {
                    indices.Add(newArgs[index]);
                }
                Debug.Assert(indices.Count + 2 == newArgs.Count);
                return(Builder.MapStore(map, value, indices.ToArray()));
            }
            else if (node.Fun is MapSelect)
            {
                // FIXME: We're probably making too many lists/arrays here
                Debug.Assert(newArgs.Count >= 2);
                var map     = newArgs[0];
                var indices = new List <Expr>();
                for (int index = 1; index < newArgs.Count; ++index)
                {
                    indices.Add(newArgs[index]);
                }
                Debug.Assert(indices.Count + 1 == newArgs.Count);
                return(Builder.MapSelect(map, indices.ToArray()));
            }
            else if (node.Fun is IfThenElse)
            {
                Debug.Assert(newArgs.Count == 3);
                return(Builder.IfThenElse(newArgs[0], newArgs[1], newArgs[2]));
            }
            else if (node.Fun is TypeCoercion)
            {
                // FIXME: Add support for this in the builder
                // I don't want to put this into the IExprBuilder until I know
                // exactly how this operator works and how it should be type checked
                var immutableTC = new NAryExpr(Token.NoToken, node.Fun, newArgs, /*immutable=*/ true);
                immutableTC.Type = node.Type;
                return(immutableTC);
            }
            else if (node.Fun is ArithmeticCoercion)
            {
                Debug.Assert(newArgs.Count == 1);
                var ac = node.Fun as ArithmeticCoercion;
                return(Builder.ArithmeticCoercion(ac.Coercion, newArgs[0]));
            }

            throw new NotSupportedException("Unsupported NAryExpr");
        }