<commit_before>namespace Equ
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    public class EqualityFunctionGenerator
    {
        private static readonly MethodInfo _objectEqualsMethod = typeof(object).GetMethod("Equals", BindingFlags.Static | BindingFlags.Public);

        private readonly Type _type;

        private readonly Func<Type, IEnumerable<FieldInfo>> _fieldSelector;

        private readonly Func<Type, IEnumerable<PropertyInfo>> _propertySelector;

        public EqualityFunctionGenerator(Type type, Func<Type, IEnumerable<FieldInfo>> fieldSelector, Func<Type, IEnumerable<PropertyInfo>> propertySelector)
        {
            _type = type;
            _fieldSelector = fieldSelector;
            _propertySelector = propertySelector;
        }

        public Func<object, int> MakeGetHashCodeMethod()
        {
            var objRaw = Expression.Parameter(typeof(object), "obj");

            // cast to the concrete type
            var objParam = Expression.Convert(objRaw, _type);

            // compound XOR expression
            var getHashCodeExprs = GetIncludedMembers(_type).Select(p => MakeGetHashCodeExpression(p.Item1, p.Item2, objParam));
            var xorChainExpr = getHashCodeExprs.Aggregate((Expression)Expression.Constant(29), LinkHashCodeExpression);
            
            return Expression.Lambda<Func<object, int>>(xorChainExpr, objRaw).Compile();
        }

        public Func<object, object, bool> MakeEqualsMethod()
        {
            var leftRaw = Expression.Parameter(typeof(object), "left");
            var rightRaw = Expression.Parameter(typeof(object), "right");

            // cast to the concrete type
            var leftParam = Expression.Convert(leftRaw, _type);
            var rightParam = Expression.Convert(rightRaw, _type);

            // compound AND expression using short-circuit evaluation
            var equalsExprs = GetIncludedMembers(_type).Select(p => MakeEqualsExpression(p.Item1, p.Item2, leftParam, rightParam));
            var andChainExpr = equalsExprs.Aggregate((Expression)Expression.Constant(true), Expression.AndAlso);

            // call Object.Equals if second parameter doesn't match type
            var objectEqualsExpr = Expression.Equal(leftRaw, rightRaw);
            var useTypedEqualsExpression = Expression.Condition(
                Expression.TypeIs(rightRaw, _type),
                andChainExpr,
                objectEqualsExpr);

            return Expression.Lambda<Func<object, object, bool>>(useTypedEqualsExpression, leftRaw, rightRaw).Compile();
        }

        private IEnumerable<Tuple<MemberInfo, Type>> GetIncludedMembers(Type type)
        {
            var selectedFields = _fieldSelector(type).Select(f => Tuple.Create((MemberInfo)f, f.FieldType));
            var selectedProperties = _propertySelector(type).Select(f => Tuple.Create((MemberInfo)f, f.PropertyType));

            return selectedFields.Concat(selectedProperties);
        }

        private static Expression LinkHashCodeExpression(Expression left, Expression right)
        {
            var leftMultiplied = Expression.Multiply(left, Expression.Constant(486187739));
            return Expression.ExclusiveOr(leftMultiplied, right);
        }

        private static Expression MakeEqualsExpression(MemberInfo member, Type memberType, Expression left, Expression right)
        {
            var leftMemberExpr = Expression.MakeMemberAccess(left, member);
            var rightMemberExpr = Expression.MakeMemberAccess(right, member);

            if (memberType.IsValueType)
            {
                return MakeValueTypeEqualExpression(leftMemberExpr, rightMemberExpr);
            }
            if (IsSequenceType(memberType))
            {
                return MakeSequenceTypeEqualExpression(leftMemberExpr, rightMemberExpr, memberType);
            }
            return MakeReferenceTypeEqualExpression(leftMemberExpr, rightMemberExpr);
        }

        private static Expression MakeValueTypeEqualExpression(Expression left, Expression right)
        {
            return Expression.Equal(left, right);
        }

        private static Expression MakeSequenceTypeEqualExpression(Expression left, Expression right, Type enumerableType)
        {
            return MakeCallOnSequenceEqualityComparerExpression("Equals", enumerableType, left, right);
        }

        private static Expression MakeReferenceTypeEqualExpression(Expression left, Expression right)
        {
            return Expression.Call(_objectEqualsMethod, left, right);
        }

        private static Expression MakeGetHashCodeExpression(MemberInfo member, Type memberType, UnaryExpression obj)
        {
            var memberAccessExpr = Expression.MakeMemberAccess(obj, member);
            var memberAccessAsObjExpr = Expression.Convert(memberAccessExpr, typeof(object));

            var getHashCodeExpr = IsSequenceType(memberType)
                ? MakeCallOnSequenceEqualityComparerExpression("GetHashCode", memberType, memberAccessExpr)
                : Expression.Call(memberAccessAsObjExpr, "GetHashCode", Type.EmptyTypes);

            return Expression.Condition(
                Expression.ReferenceEqual(Expression.Constant(null), memberAccessAsObjExpr), // If member is null
                Expression.Constant(0), // Return 0
                getHashCodeExpr); // Return the actual getHashCode call
        }

        private static Expression MakeCallOnSequenceEqualityComparerExpression(string methodName, Type enumerableType, params Expression[] parameterExpressions)
        {
            var comparerType = typeof(ElementwiseSequenceEqualityComparer<>).MakeGenericType(enumerableType);
            var comparerInstance = comparerType.GetProperty("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null);
            var comparerExpr = Expression.Constant(comparerInstance);

            return Expression.Call(comparerExpr, methodName, Type.EmptyTypes, parameterExpressions);
        }

        private static bool IsSequenceType(Type type)
        {
            return typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
        }
    }<commit_msg>Improvements from svick (code review) carried out.<commit_after>namespace Equ
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    public class EqualityFunctionGenerator
    {
        private static readonly MethodInfo _objectEqualsMethod = new Func<object, object, bool>(Equals).Method;

        private readonly Type _type;

        private readonly Func<Type, IEnumerable<FieldInfo>> _fieldSelector;

        private readonly Func<Type, IEnumerable<PropertyInfo>> _propertySelector;

        public EqualityFunctionGenerator(Type type, Func<Type, IEnumerable<FieldInfo>> fieldSelector, Func<Type, IEnumerable<PropertyInfo>> propertySelector)
        {
            _type = type;
            _fieldSelector = fieldSelector;
            _propertySelector = propertySelector;
        }

        public Func<object, int> MakeGetHashCodeMethod()
        {
            var objRaw = Expression.Parameter(typeof(object), "obj");

            // cast to the concrete type
            var objParam = Expression.Convert(objRaw, _type);

            // compound XOR expression
            var getHashCodeExprs = GetIncludedMembers(_type).Select(mi => MakeGetHashCodeExpression(mi, objParam));
            var xorChainExpr = getHashCodeExprs.Aggregate((Expression)Expression.Constant(29), LinkHashCodeExpression);
            
            return Expression.Lambda<Func<object, int>>(xorChainExpr, objRaw).Compile();
        }

        public Func<object, object, bool> MakeEqualsMethod()
        {
            var leftRaw = Expression.Parameter(typeof(object), "left");
            var rightRaw = Expression.Parameter(typeof(object), "right");

            // cast to the concrete type
            var leftParam = Expression.Convert(leftRaw, _type);
            var rightParam = Expression.Convert(rightRaw, _type);

            // AND expression using short-circuit evaluation
            var equalsExprs = GetIncludedMembers(_type).Select(mi => MakeEqualsExpression(mi, leftParam, rightParam));
            var andChainExpr = equalsExprs.Aggregate(Expression.AndAlso);

            // call Object.Equals if second parameter doesn't match type
            var objectEqualsExpr = Expression.Equal(leftRaw, rightRaw);
            var useTypedEqualsExpression = Expression.Condition(
                Expression.TypeIs(rightRaw, _type),
                andChainExpr,
                objectEqualsExpr);

            return Expression.Lambda<Func<object, object, bool>>(useTypedEqualsExpression, leftRaw, rightRaw).Compile();
        }

        private IEnumerable<MemberInfo> GetIncludedMembers(Type type)
        {
            return _fieldSelector(type).Cast<MemberInfo>().Concat(_propertySelector(type));
        }

        private static Expression LinkHashCodeExpression(Expression left, Expression right)
        {
            var leftMultiplied = Expression.Multiply(left, Expression.Constant(486187739));
            return Expression.ExclusiveOr(leftMultiplied, right);
        }

        private static Expression MakeEqualsExpression(MemberInfo member, Expression left, Expression right)
        {
            var leftMemberExpr = Expression.MakeMemberAccess(left, member);
            var rightMemberExpr = Expression.MakeMemberAccess(right, member);

            var memberType = leftMemberExpr.Type;

            if (leftMemberExpr.Type.IsValueType)
            {
                return MakeValueTypeEqualExpression(leftMemberExpr, rightMemberExpr);
            }
            if (IsSequenceType(memberType))
            {
                return MakeSequenceTypeEqualExpression(leftMemberExpr, rightMemberExpr, memberType);
            }
            return MakeReferenceTypeEqualExpression(leftMemberExpr, rightMemberExpr);
        }

        private static Expression MakeValueTypeEqualExpression(Expression left, Expression right)
        {
            return Expression.Equal(left, right);
        }

        private static Expression MakeSequenceTypeEqualExpression(Expression left, Expression right, Type enumerableType)
        {
            return MakeCallOnSequenceEqualityComparerExpression("Equals", enumerableType, left, right);
        }

        private static Expression MakeReferenceTypeEqualExpression(Expression left, Expression right)
        {
            return Expression.Call(_objectEqualsMethod, left, right);
        }

        private static Expression MakeGetHashCodeExpression(MemberInfo member, Expression obj)
        {
            var memberAccessExpr = Expression.MakeMemberAccess(obj, member);
            var memberAccessAsObjExpr = Expression.Convert(memberAccessExpr, typeof(object));

            var memberType = memberAccessExpr.Type;

            var getHashCodeExpr = IsSequenceType(memberType)
                ? MakeCallOnSequenceEqualityComparerExpression("GetHashCode", memberType, memberAccessExpr)
                : Expression.Call(memberAccessAsObjExpr, "GetHashCode", Type.EmptyTypes);

            return Expression.Condition(
                Expression.ReferenceEqual(Expression.Constant(null), memberAccessAsObjExpr), // If member is null
                Expression.Constant(0), // Return 0
                getHashCodeExpr); // Return the actual getHashCode call
        }

        private static Expression MakeCallOnSequenceEqualityComparerExpression(string methodName, Type enumerableType, params Expression[] parameterExpressions)
        {
            var comparerType = typeof(ElementwiseSequenceEqualityComparer<>).MakeGenericType(enumerableType);
            var comparerInstance = comparerType.GetProperty("Default", BindingFlags.Static | BindingFlags.Public).GetValue(null);
            var comparerExpr = Expression.Constant(comparerInstance);

            return Expression.Call(comparerExpr, methodName, Type.EmptyTypes, parameterExpressions);
        }

        private static bool IsSequenceType(Type type)
        {
            return typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
        }
    }
{'fraction_non_alphanumeric': 0.07868694182959209, 'fraction_numerical': 0.0024137098720733766, 'mean_word_length': 2.8447262604392205, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6257687', 'n_tokens_mistral': 2946, 'n_tokens_neox': 2846, 'n_words': 660}