/// <summary>
        /// Determines whether two unaries are similar.
        /// </summary>
        /// <remarks>
        /// Two instances of <see cref="UnaryExpression"/> are similar when they share the same
        /// <see cref="ExpressionType"/>, method information, and when their operands pass
        /// equivalency.
        /// </remarks>
        /// <param name="source">The source <see cref="UnaryExpression"/>.</param>
        /// <param name="target">The target <see cref="UnaryExpression"/>.</param>
        /// <returns>A flag that indicates whether the two expressions are Similar.</returns>
        private bool UnariesAreSimilar(
            UnaryExpression source,
            UnaryExpression target)
        {
            if (source.NodeType != target.NodeType)
            {
                return(false);
            }

            if ((source.Method != null && target.Method == null) ||
                (source.Method == null && target.Method != null))
            {
                return(false);
            }

            if (source.Method != null)
            {
                if (!ExpressionSimilarity.TypesAreSimilar(source.Method.DeclaringType, target.Method.DeclaringType) ||
                    source.Method.Name != target.Method.Name)
                {
                    return(false);
                }
            }

            return(ExpressionSimilarity.IsPartOf(source.Operand, target.Operand));
        }
        /// <summary>
        /// Check for equivalent array initializers.
        /// </summary>
        /// <remarks>
        /// To be true, type must be equivalent and expressions must be similar.
        /// </remarks>
        /// <param name="source">The source <see cref="NewArrayExpression"/>.</param>
        /// <param name="target">The target <see cref="NewArrayExpression"/>.</param>
        /// <returns>A flag indicating whether the array initializers are equivalent.</returns>
        private bool NewArraysAreSimilar(
            NewArrayExpression source,
            NewArrayExpression target)
        {
            if (source.Type != target.Type)
            {
                return(false);
            }

            return(ExpressionSimilarity.AreSimilar(source.Expressions, target.Expressions));
        }
        /// <summary>
        /// Determines whether two binaries are similar.
        /// </summary>
        /// <remarks>
        /// Two instances of <see cref="BinaryExpression"/> are similar when they share the same
        /// <see cref="ExpressionType"/> and the recursive determination of the left expressoin and
        /// the right expressions is similar.
        /// </remarks>
        /// <param name="source">The source <see cref="BinaryExpression"/>.</param>
        /// <param name="target">The target <see cref="BinaryExpression"/>.</param>
        /// <returns>A flag that indicates whether the two expressions are similar.</returns>
        private bool BinariesAreSimilar(
            BinaryExpression source,
            BinaryExpression target)
        {
            if (source.NodeType != target.NodeType)
            {
                return(false);
            }

            return(ExpressionSimilarity.IsPartOf(source.Left, target.Left) &&
                   ExpressionSimilarity.IsPartOf(source.Right, target.Right));
        }
        /// <summary>
        /// Determine whether two members are similar.
        /// </summary>
        /// <remarks>
        /// Two instances of <see cref="MemberExpression"/> are similar
        /// when they share the same type (this will match the member type), the same
        /// declaring type, the same name, and if there is an expression, the
        /// expressions are similar.
        /// </remarks>
        /// <param name="source">The source <see cref="MemberExpression"/>.</param>
        /// <param name="target">The target <see cref="MemberExpression"/>.</param>
        /// <returns>A flag indicating whether the two expressions are Similar.</returns>
        private bool MembersAreSimilar(
            MemberExpression source,
            MemberExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type) ||
                !ExpressionSimilarity.TypesAreSimilar(source.Member.DeclaringType, target.Member.DeclaringType) ||
                source.Member.Name != target.Member.Name)
            {
                return(false);
            }

            return(source.Expression == null ||
                   ExpressionSimilarity.IsPartOf(source.Expression, target.Expression));
        }
        /// <summary>
        /// Method to compare two <seealso cref="ConstantExpression"/>
        /// instances.
        /// </summary>
        /// <remarks>
        /// If the constant is an expression, similarity is recursed. If it is a value type,
        /// the source and target must be equal. Otherwise, similar is based on types.
        /// </remarks>
        /// <param name="source">The source <see cref="ConstantExpression"/>.</param>
        /// <param name="target">The target <see cref="ConstantExpression"/>.</param>
        /// <returns>A flag indicating whether the two are similar.</returns>
        private bool ConstantsAreSimilar(
            ConstantExpression source,
            ConstantExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, typeof(Expression)) &&
                !ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type))
            {
                return(false);
            }

            if (source.Value == null)
            {
                return(target.Value == null);
            }

            if (source.Value is Expression expression)
            {
                return(ExpressionSimilarity.AreSimilar(
                           expression,
                           target.Value as Expression));
            }

            // lists
            if (typeof(Array).IsAssignableFrom(source.Type) ||
                typeof(System.Collections.ICollection).IsAssignableFrom(source.Type))
            {
                return(true);
            }

            // string
            if (source.Type == typeof(string))
            {
                return(source.Value == target.Value);
            }

            // typed collections
            if (source.Type.GetInterfaces()
                .Any(i => i.IsGenericType &&
                     i.GetGenericTypeDefinition() == typeof(IEnumerable <>)))
            {
                return(true);
            }

            // "primitives"
            return(eq.ValuesAreEquivalent(
                       source.Value,
                       target.Value));
        }
        /// <summary>
        /// Check for similar member initialization.
        /// </summary>
        /// <remarks>
        /// The expression must be similar, the types similar, and every binding must be equivalent.
        /// </remarks>
        /// <param name="source">The source <see cref="MemberInitExpression"/>.</param>
        /// <param name="target">The target <see cref="MemberInitExpression"/>.</param>
        /// <returns>A flag indicating whether the member initializations are similar.</returns>
        private bool MemberInitAreSimilar(
            MemberInitExpression source,
            MemberInitExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type))
            {
                return(false);
            }

            if (!ExpressionSimilarity.AreSimilar(source.NewExpression, target.NewExpression))
            {
                return(false);
            }

            return(ExpressionSimilarity.MemberBindingsAreSimilar(source.Bindings, target.Bindings));
        }
        /// <summary>
        /// Determine whether two methods are similar.
        /// </summary>
        /// <remarks>
        /// Two metods are similar when they share the same return type, declaring type,
        /// the same name, are either both instance or static fields, and all
        /// arguments pass similarity. Arguments of same expression type are tested
        /// for similarity. Arguments of different expression types are tested for the
        /// same return type.
        /// </remarks>
        /// <param name="source">The source <see cref="MethodCallExpression"/>.</param>
        /// <param name="target">The target <see cref="MethodCallExpression"/>.</param>
        /// <returns>A flag indicating whether the two expressions are Similar.</returns>
        private bool MethodsAreSimilar(
            MethodCallExpression source,
            MethodCallExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type) ||
                !ExpressionSimilarity.TypesAreSimilar(source.Method.DeclaringType, target.Method.DeclaringType) ||
                source.Method.Name != target.Method.Name)
            {
                return(false);
            }

            if (source.Arguments.Count != target.Arguments.Count)
            {
                return(false);
            }

            // always null when static
            if (source.Object != null)
            {
                if (!ExpressionSimilarity.IsPartOf(source.Object, target.Object))
                {
                    return(false);
                }
            }

            for (var idx = 0; idx < source.Arguments.Count; idx += 1)
            {
                bool similar;
                if (source.Arguments[idx].GetType() == target.Arguments[idx].GetType())
                {
                    similar = ExpressionSimilarity.IsPartOf(source.Arguments[idx], target.Arguments[idx]);
                }
                else
                {
                    similar = ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type);
                }

                if (!similar)
                {
                    return(false);
                }
            }

            return(true);
        }
        /// <summary>
        /// Check for similar object initializer.
        /// </summary>
        /// <remarks>
        /// To be true, type must be similar,
        /// parameters are similar and arguments are similar.
        /// </remarks>
        /// <param name="source">The source <see cref="NewExpression"/>.</param>
        /// <param name="target">The target <see cref="NewExpression"/>.</param>
        /// <returns>A flag indicating whether the object initializers are equivalent.</returns>
        private bool NewAreSimilar(
            NewExpression source,
            NewExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type))
            {
                return(false);
            }

            if (!ExpressionSimilarity.ParameterInfosAreSimilar(
                    source.Constructor.GetParameters(),
                    target.Constructor.GetParameters()))
            {
                return(false);
            }

            return(ExpressionSimilarity.AreSimilar(source.Arguments, target.Arguments));
        }
        /// <summary>
        /// Method to compare two <see cref="InvocationExpression"/> for similarty.
        /// </summary>
        /// <remarks>
        /// To be similar, types must be assignable, arguments must be similar,
        /// and the source expression must be part of the target expression.
        /// </remarks>
        /// <param name="source">The source <see cref="InvocationExpression"/>.</param>
        /// <param name="target">The target <see cref="InvocationExpression"/>.</param>
        /// <returns>A value that indicates whether they are similar.</returns>
        private bool InvocationsAreSimilar(
            InvocationExpression source,
            InvocationExpression target)
        {
            if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type))
            {
                return(false);
            }

            if (source.Arguments.Count != target.Arguments.Count)
            {
                return(false);
            }

            if (!ExpressionSimilarity.AreSimilar(source.Arguments, target.Arguments))
            {
                return(false);
            }

            return(ExpressionSimilarity.IsPartOf(source.Expression, target.Expression));
        }
        /// <summary>
        /// Determine whether two lambdas are similar.
        /// </summary>
        /// <remarks>
        /// Two lambda expressions are similar when they share the same name,
        /// similar parameters, and when both body and parameters are similar.
        /// </remarks>
        /// <param name="source">The source <see cref="LambdaExpression"/>.</param>
        /// <param name="target">The target <see cref="LambdaExpression"/>.</param>
        /// <returns>A flag indicating whether the two expressions are similar.</returns>
        private bool LambdasAreSimilar(
            LambdaExpression source,
            LambdaExpression target)
        {
            if (source.Name != target.Name)
            {
                return(false);
            }

            if (source.Parameters.Count != target.Parameters.Count)
            {
                return(false);
            }

            if (!ExpressionSimilarity.AreSimilar(source.Parameters, target.Parameters))
            {
                return(false);
            }

            return(ExpressionSimilarity.IsPartOf(source.Body, target.Body));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultHighPerformanceRules"/> class.
        /// </summary>
        public DefaultHighPerformanceRules()
        {
            equivalencyRules =
                new Dictionary <Type, Func <Expression, Expression, bool> >
            {
                { typeof(LambdaExpression), (s, t) => ExecuteRuleAs <LambdaExpression>(s, t, LambdasAreEquivalent) },
                { typeof(ConstantExpression), (s, t) => ExecuteRuleAs <ConstantExpression>(s, t, ConstantsAreEquivalent) },
                { typeof(InvocationExpression), (s, t) => ExecuteRuleAs <InvocationExpression>(s, t, InvocationsAreEquivalent) },
                { typeof(MemberExpression), (s, t) => ExecuteRuleAs <MemberExpression>(s, t, MembersAreEquivalent) },
                { typeof(MethodCallExpression), (s, t) => ExecuteRuleAs <MethodCallExpression>(s, t, MethodsAreEquivalent) },
                { typeof(BinaryExpression), (s, t) => ExecuteRuleAs <BinaryExpression>(s, t, BinariesAreEquivalent) },
                { typeof(NewArrayExpression), (s, t) => ExecuteRuleAs <NewArrayExpression>(s, t, NewArraysAreEquivalent) },
                { typeof(NewExpression), (s, t) => ExecuteRuleAs <NewExpression>(s, t, NewAreEquivalent) },
                { typeof(ParameterExpression), (s, t) => ExecuteRuleAs <ParameterExpression>(s, t, ParametersAreEquivalent) },
                { typeof(UnaryExpression), (s, t) => ExecuteRuleAs <UnaryExpression>(s, t, UnariesAreEquivalent) },
                { typeof(MemberInitExpression), (s, t) => ExecuteRuleAs <MemberInitExpression>(s, t, MemberInitAreEquivalent) },
            };

            similarityRules =
                new Dictionary <Type, Func <Expression, Expression, bool> >
            {
                { typeof(LambdaExpression), (s, t) => ExecuteRuleAs <LambdaExpression>(s, t, LambdasAreSimilar) },
                { typeof(ConstantExpression), (s, t) => ExecuteRuleAs <ConstantExpression>(s, t, ConstantsAreSimilar) },
                { typeof(MemberExpression), (s, t) => ExecuteRuleAs <MemberExpression>(s, t, MembersAreSimilar) },
                { typeof(InvocationExpression), (s, t) => ExecuteRuleAs <InvocationExpression>(s, t, InvocationsAreSimilar) },
                { typeof(MethodCallExpression), (s, t) => ExecuteRuleAs <MethodCallExpression>(s, t, MethodsAreSimilar) },
                { typeof(BinaryExpression), (s, t) => ExecuteRuleAs <BinaryExpression>(s, t, BinariesAreSimilar) },
                { typeof(NewArrayExpression), (s, t) => ExecuteRuleAs <NewArrayExpression>(s, t, NewArraysAreSimilar) },
                { typeof(NewExpression), (s, t) => ExecuteRuleAs <NewExpression>(s, t, NewAreSimilar) },
                {
                    typeof(ParameterExpression), (s, t) => ExecuteRuleAs <ParameterExpression>(
                        s,
                        t,
                        (src, tgt) => ExpressionSimilarity.TypesAreSimilar(src.Type, tgt.Type))
                },
                { typeof(UnaryExpression), (s, t) => ExecuteRuleAs <UnaryExpression>(s, t, UnariesAreSimilar) },
                { typeof(MemberInitExpression), (s, t) => ExecuteRuleAs <MemberInitExpression>(s, t, MemberInitAreSimilar) },
            };
        }
 /// <summary>
 /// Determines whether an <see cref="IQueryable{T}"/> is part of another query.
 /// </summary>
 /// <remarks>
 /// A source is part of a target if an <see cref="Expression"/> exists in the
 /// target's tree that is similar to the source.
 /// </remarks>
 /// <typeparam name="T">The <see cref="Type"/> of the entity.</typeparam>
 /// <param name="source">The source <see cref="IQueryable{T}"/>.</param>
 /// <param name="target">The target <see cref="IQueryable{T}"/> to parse.</param>
 /// <returns>A flag indicating whether the source is part of the target.</returns>
 public bool IsPartOf <T>(IQueryable <T> source, IQueryable <T> target) =>
 ExpressionSimilarity.IsPartOf(source?.Expression, target?.Expression);
 /// <summary>
 /// Determines whether an <see cref="Expression"/> is part of another expression.
 /// </summary>
 /// <remarks>
 /// A source is part of a target if an <see cref="Expression"/> exists in the
 /// target's tree that is similar to the source.
 /// </remarks>
 /// <param name="source">The source <see cref="Expression"/>.</param>
 /// <param name="target">The target <see cref="Expression"/> to parse.</param>
 /// <returns>A flag indicating whether the source is part of the target.</returns>
 public bool IsPartOf(Expression source, Expression target) =>
 ExpressionSimilarity.IsPartOf(source, target);
 /// <summary>
 /// Comparison of multiple expressions. Similar
 /// only when all elements match, in order, and
 /// pass the similarity test. It's fine if the
 /// source does not have the same number of entities
 /// as the target.
 /// </summary>
 /// <param name="source">The source expressions.</param>
 /// <param name="target">The target expressions.</param>
 /// <returns>A flag indicating whether the two sets of
 /// expressions are Similar.</returns>
 public bool AreSimilar(
     IEnumerable <Expression> source,
     IEnumerable <Expression> target) =>
 ExpressionSimilarity.AreSimilar(source, target);
 /// <summary>
 /// Entry for similarity comparisons. Will cast to
 /// known types and compare.
 /// </summary>
 /// <typeparam name="T">The <see cref="Type"/> of entity.</typeparam>
 /// <param name="source">The source <see cref="IQueryable{T}"/>.</param>
 /// <param name="target">The target <see cref="IQueryable{T}"/> to compare to.</param>
 /// <returns>A flag indicating whether the source and target are similar.</returns>
 public bool AreSimilar <T>(IQueryable <T> source, IQueryable <T> target) =>
 ExpressionSimilarity.AreSimilar(source?.Expression, target?.Expression);
 /// <summary>
 /// Entry for similarity comparisons. Will cast to
 /// known types and compare.
 /// </summary>
 /// <param name="source">The source <see cref="Expression"/>.</param>
 /// <param name="target">The target <see cref="Expression"/> to compare to.</param>
 /// <returns>A flag indicating whether the source and target are similar.</returns>
 public bool AreSimilar(Expression source, Expression target) =>
 ExpressionSimilarity.AreSimilar(source, target);