/// <summary>
        /// Determines whether a typed data can do certain operation with another typed data
        /// </summary>
        /// <param name="operation">the operation</param>
        /// <param name="sourceType">the type of data to operation on</param>
        /// <param name="otherType">the other type</param>
        /// <returns>Value <c>true</c> if operation can be performed; otherwise, <c>false</c>.</returns>
        public virtual bool Supports(QueryBinaryOperation operation, QueryScalarType sourceType, QueryScalarType otherType)
        {
            var sourceQueryClrType = (IQueryClrType)sourceType;
            var otherQueryClrType  = (IQueryClrType)otherType;
            var sourceClrType      = sourceQueryClrType.ClrType;
            var otherClrType       = otherQueryClrType.ClrType;

            switch (operation)
            {
            case QueryBinaryOperation.Add:
            case QueryBinaryOperation.Divide:
            case QueryBinaryOperation.Modulo:
            case QueryBinaryOperation.Multiply:
            case QueryBinaryOperation.Subtract:
                return(LinqTypeSemantics.IsNumeric(sourceClrType) &&
                       LinqTypeSemantics.IsNumeric(otherClrType) &&
                       this.HaveCommonType(sourceClrType, otherClrType));

            case QueryBinaryOperation.BitwiseAnd:
            case QueryBinaryOperation.BitwiseExclusiveOr:
            case QueryBinaryOperation.BitwiseOr:
                return(LinqTypeSemantics.IsIntegralType(sourceClrType) &&
                       LinqTypeSemantics.IsIntegralType(otherClrType) &&
                       this.HaveCommonType(sourceClrType, otherClrType));

            case QueryBinaryOperation.LogicalAnd:
            case QueryBinaryOperation.LogicalOr:
                return(sourceClrType == typeof(bool) && otherClrType == typeof(bool));

            case QueryBinaryOperation.EqualTo:
            case QueryBinaryOperation.NotEqualTo:
                return(this.HaveCommonType(sourceClrType, otherClrType));

            case QueryBinaryOperation.LessThan:
            case QueryBinaryOperation.LessThanOrEqualTo:
            case QueryBinaryOperation.GreaterThan:
            case QueryBinaryOperation.GreaterThanOrEqualTo:
                if (sourceClrType == typeof(byte[]) || otherClrType == typeof(byte[]) ||
                    sourceClrType == typeof(string) || otherClrType == typeof(string) ||
                    sourceClrType == typeof(bool) || otherClrType == typeof(bool) ||
                    sourceClrType == typeof(bool?) || otherClrType == typeof(bool?) ||
                    this.IsSpatialType(sourceQueryClrType) || this.IsSpatialType(otherQueryClrType))
                {
                    return(false);
                }
                else
                {
                    return(this.HaveCommonType(sourceClrType, otherClrType));
                }

            case QueryBinaryOperation.Concat:
                return(sourceClrType == typeof(string) || otherClrType == typeof(string));

            default:
                throw new TaupoNotSupportedException("Unsupported query binary operation.");
            }
        }
        private bool HaveCommonType(Type t1, Type t2)
        {
            Type commonClrType;

            return(LinqTypeSemantics.TryGetCommonType(t1, t2, out commonClrType));
        }
        /// <summary>
        /// Gets the common type to which both types can be promoted.
        /// </summary>
        /// <param name="leftType">First type.</param>
        /// <param name="rightType">Second type.</param>
        /// <returns>
        /// Common type to which both types can be promoted. Throws if unable to find a common type.
        /// </returns>
        public QueryScalarType GetCommonType(QueryScalarType leftType, QueryScalarType rightType)
        {
            Type commonClrType = LinqTypeSemantics.GetCommonType(((QueryClrPrimitiveType)leftType).ClrType, ((QueryClrPrimitiveType)rightType).ClrType);

            return(new QueryClrPrimitiveType(commonClrType, this));
        }
        /// <summary>
        /// Compares the primitive value to another value and returns their relative ordering.
        /// </summary>
        /// <param name="value">First value.</param>
        /// <param name="otherValue">Second value.</param>
        /// <returns>
        /// Integer which is less than zero if this value is less than the other value, 0 if they are equal,
        /// greater than zero if this value is greater than the other value
        /// </returns>
        public int Compare(QueryScalarValue value, QueryScalarValue otherValue)
        {
            var    t1 = (QueryClrPrimitiveType)value.Type;
            var    t2 = (QueryClrPrimitiveType)otherValue.Type;
            object v1 = value.Value;
            object v2 = otherValue.Value;

            if (this.IsSpatialType(t1))
            {
                ExceptionUtilities.Assert(this.IsSpatialType(t2), "Because CLR types match, both values should both be spatial. Type was '{0}'", t2);
                ExceptionUtilities.CheckObjectNotNull(this.SpatialEqualityComparer, "Cannot compare spatial values without a spatial equality-comparer");
                return(this.SpatialEqualityComparer.Equals(v1, v2) ? 0 : 2);
            }

            if (t1.ClrType != t2.ClrType)
            {
                Type commonClrType = LinqTypeSemantics.GetCommonType(t1.ClrType, t2.ClrType);
                commonClrType = Nullable.GetUnderlyingType(commonClrType) ?? commonClrType;

                if (v1 != null)
                {
                    v1 = ArithmeticEvaluationHelper.ChangeType(v1, commonClrType);
                }

                if (v2 != null)
                {
                    v2 = ArithmeticEvaluationHelper.ChangeType(v2, commonClrType);
                }
            }

            if (v1 == null)
            {
                if (v2 == null)
                {
                    // both null - are equal
                    return(0);
                }
                else
                {
                    // first null, second not-null
                    return(-1);
                }
            }
            else
            {
                if (v2 == null)
                {
                    // first not null, second null
                    return(1);
                }
            }

            var bytes1 = v1 as byte[];

            if (bytes1 != null)
            {
                var bytes2 = (byte[])v2;

                return(CompareByteArrays(bytes1, bytes2));
            }

            IComparable cv1 = (IComparable)v1;

            return(cv1.CompareTo(v2));
        }