private SqlExpression SimplifyUnaryExpression(
            ExpressionType operatorType,
            SqlExpression operand,
            Type type,
            RelationalTypeMapping typeMapping)
        {
            switch (operatorType)
            {
            case ExpressionType.Not
                when type == typeof(bool):
            {
                switch (operand)
                {
                // !(true) -> false
                // !(false) -> true
                case SqlConstantExpression constantOperand
                    when constantOperand.Value is bool value:
                {
                    return(_sqlExpressionFactory.Constant(!value, typeMapping));
                }

                case InExpression inOperand:
                    return(inOperand.Negate());

                case SqlUnaryExpression unaryOperand:
                    switch (unaryOperand.OperatorType)
                    {
                    // !(!a) -> a
                    case ExpressionType.Not:
                        return(unaryOperand.Operand);

                    //!(a IS NULL) -> a IS NOT NULL
                    case ExpressionType.Equal:
                        return(_sqlExpressionFactory.IsNotNull(unaryOperand.Operand));

                    //!(a IS NOT NULL) -> a IS NULL
                    case ExpressionType.NotEqual:
                        return(_sqlExpressionFactory.IsNull(unaryOperand.Operand));
                    }

                    break;

                case SqlBinaryExpression binaryOperand:
                {
                    // De Morgan's
                    if (binaryOperand.OperatorType == ExpressionType.AndAlso ||
                        binaryOperand.OperatorType == ExpressionType.OrElse)
                    {
                        var newLeft  = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Left, type, typeMapping);
                        var newRight = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Right, type, typeMapping);

                        return(SimplifyLogicalSqlBinaryExpression(
                                   binaryOperand.OperatorType == ExpressionType.AndAlso
                                        ? ExpressionType.OrElse
                                        : ExpressionType.AndAlso,
                                   newLeft,
                                   newRight,
                                   binaryOperand.TypeMapping !));
                    }

                    // those optimizations are only valid in 2-value logic
                    // they are safe to do here because if we apply null semantics
                    // because null semantics removes possibility of nulls in the tree when the comparison is wrapped around NOT
                    if (!_useRelationalNulls &&
                        TryNegate(binaryOperand.OperatorType, out var negated))
                    {
                        return(SimplifyBinaryExpression(
                                   negated,
                                   binaryOperand.Left,
                                   binaryOperand.Right,
                                   binaryOperand.TypeMapping !));
                    }
                }
                break;
                }

                break;
            }

            case ExpressionType.Equal:
            case ExpressionType.NotEqual:
                return(SimplifyNullNotNullExpression(
                           operatorType,
                           operand,
                           type,
                           typeMapping));
            }

            return(_sqlExpressionFactory.MakeUnary(operatorType, operand, type, typeMapping) !);
        }