/// <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>
        /// 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>
        /// 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>
 /// 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);