<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace GraphClimber
{
    public class GenericArgumentBinder : IGenericArgumentBinder
    {
        private class ArgumentMapper
        {
            private readonly IDictionary<Type, HashSet<Type>> _genericConstraintsToType;

            public ArgumentMapper(MethodInfo methodInfo)
            {
                if (methodInfo.ContainsGenericParameters)
                {
                    _genericConstraintsToType =
                        methodInfo.GetGenericArguments()
                            .ToDictionary(type => type, t => new HashSet<Type>());
                }
            }

            /// <summary>
            /// This method tries to find available bindings
            /// from the given <paramref name="parameterType"/>
            /// to the given <paramref name="realType"/>.
            /// </summary>
            /// <remarks>
            /// This method mutates the return value of <see cref="GetGenericArgumentsCandidates"/>,
            /// So we have to get inside all the available paths, That's why return value isn't returned at
            /// the first place, and only in the end of the method.
            /// </remarks>
            /// <param name="parameterType"></param>
            /// <param name="realType"></param>
            /// <returns></returns>
            public bool TryBind(Type parameterType, Type realType)
            {
                return InnerTryBind(parameterType, realType,
                    new HashSet<Tuple<Type, Type>>());
            }

            private bool InnerTryBind(Type parameterType, Type realType, 
                ISet<Tuple<Type, Type>> visitedTypes)
            {
                // If we already tried this pair, assume its ok.
                // it will fail later in TryMakeGenericMethod
                if (!visitedTypes.Add(Tuple.Create(parameterType, realType)))
                {
                    return true;
                }

                if (parameterType.IsAssignableFrom(realType))
                {
                    return true;
                }

                bool result = false;

                if (parameterType.IsGenericParameter &&
                    VerifyGenericConstraints(parameterType, realType,
                    visitedTypes))
                {
                    _genericConstraintsToType[parameterType].Add(realType);
                    result = true;
                }

                if (parameterType.IsGenericType &&
                    VerifyGenericTypesAreCompatible(parameterType, realType,
                    visitedTypes))
                {
                    result = true;
                }

                if (parameterType.IsArray &&
                    VerifyArrayTypesAreCompatible(parameterType, realType,
                    visitedTypes))
                {
                    result = true;
                }

                // Try binding to base types too.
                foreach (Type intefaceType in realType.GetInterfacesAndBase())
                {
                    if (InnerTryBind(parameterType, intefaceType, visitedTypes))
                    {
                        result = true;
                    }
                }

                return result;
            }


            /// <summary>
            /// Asserts generic arguments for special attributes : new(), class, struct
            /// </summary>
            /// <param name="genericParameterType"></param>
            /// <param name="realType"></param>
            /// <param name="visitedTypes"></param>
            /// <returns></returns>
            private bool VerifyGenericConstraints(Type genericParameterType, Type realType, ISet<Tuple<Type, Type>> visitedTypes)
            {
                // Bind constraints to real type 
                // (Some generic parameters appear only as constraints)
                return genericParameterType.GetGenericParameterConstraints()
                    .All(constraint => InnerTryBind(constraint, realType, visitedTypes));
            }

            private bool VerifyGenericTypesAreCompatible(Type genericType, Type realType, ISet<Tuple<Type, Type>> visitedTypes)
            {
                IEnumerable<Type> implementations =
                    realType
                        .GetClosedGenericTypeImplementation
                        (genericType.GetGenericTypeDefinition());

                return
                    implementations.Where(implementation => VerifyGenericImplementationIsCompatible(genericType, implementation, visitedTypes))
                        .ToList()
                        .Any();
            }

            private bool VerifyGenericImplementationIsCompatible(Type genericType, Type implementation, ISet<Tuple<Type, Type>> visitedTypes)
            {
                Type[] implementationArguments = implementation.GetGenericArguments();
                Type[] staticArguments = genericType.GetGenericArguments();

                for (int i = 0; i < implementationArguments.Length; i++)
                {
                    if (!InnerTryBind(staticArguments[i], implementationArguments[i], visitedTypes))
                    {
                        return false;
                    }
                }

                return true;
            }

            /// <summary>
            /// Asserts that the given <paramref name="parameterType"/> and
            /// <paramref name="realType"/> are both compatible in case
            /// that they are arrays.
            /// </summary>
            /// <param name="parameterType"></param>
            /// <param name="realType"></param>
            /// <param name="visitedTypes"></param>
            /// <returns></returns>
            private bool VerifyArrayTypesAreCompatible(Type parameterType, Type realType, ISet<Tuple<Type, Type>> visitedTypes)
            {
                if (parameterType.IsArray && realType.IsArray)
                {
                    if (parameterType.GetArrayRank() != realType.GetArrayRank())
                    {
                        return false;
                    }

                    if (!InnerTryBind(parameterType.GetElementType(), realType.GetElementType(), visitedTypes))
                    {
                        return false;
                    }
                }

                return true;
            }

            public IEnumerable<Type[]> GetGenericArgumentsCandidates()
            {
                IEnumerable<IEnumerable<Type>> product =
                    Combinatorics.CartesianProduct(_genericConstraintsToType.Values);

                foreach (IEnumerable<Type> candidate in product)<commit_msg>Made the visited types a member of the mapper<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace GraphClimber
{
    public class GenericArgumentBinder : IGenericArgumentBinder
    {
        private class ArgumentMapper
        {
            private enum BindStatus
            {
                Visiting,
                Matched,
                NotMatched
            }

            private readonly IDictionary<Type, HashSet<Type>> _genericConstraintsToType;
            
            private readonly IDictionary<Tuple<Type, Type>, BindStatus> _visitedTypes =
                new Dictionary<Tuple<Type, Type>, BindStatus>();

            public ArgumentMapper(MethodInfo methodInfo)
            {
                if (methodInfo.ContainsGenericParameters)
                {
                    _genericConstraintsToType =
                        methodInfo.GetGenericArguments()
                            .ToDictionary(type => type, t => new HashSet<Type>());
                }
            }

            /// <summary>
            /// This method tries to find available bindings
            /// from the given <paramref name="parameterType"/>
            /// to the given <paramref name="realType"/>.
            /// </summary>
            /// <param name="parameterType"></param>
            /// <param name="realType"></param>
            /// <returns></returns>
            public bool TryBind(Type parameterType, Type realType)
            {
                // If we already tried this pair, and it is currently visited,
                // assume it is ok,
                // it might fail later in TryMakeGenericMethod
                BindStatus status;

                Tuple<Type, Type> currentPair = Tuple.Create(parameterType, realType);

                if (_visitedTypes.TryGetValue(currentPair, out status))
                {
                    return (status != BindStatus.NotMatched);
                }
                
                _visitedTypes[currentPair] = BindStatus.Visiting;

                bool result = InnerTryBind(parameterType, realType);

                if (result)
                {
                    _visitedTypes[currentPair] = BindStatus.Matched;
                }
                else
                {
                    _visitedTypes[currentPair] = BindStatus.NotMatched;
                }

                return result;
            }

            /// <remarks>
            /// This method mutates the return value of <see cref="GetGenericArgumentsCandidates"/>,
            /// So we have to get inside all the available paths, That's why return value isn't returned at
            /// the first place, and only in the end of the method.
            /// </remarks>
            private bool InnerTryBind(Type parameterType, Type realType)
            {
                if (parameterType.IsAssignableFrom(realType))
                {
                    return true;
                }

                bool result = false;

                if (parameterType.IsGenericParameter &&
                    VerifyGenericConstraints(parameterType, realType))
                {
                    _genericConstraintsToType[parameterType].Add(realType);
                    result = true;
                }

                if (parameterType.IsGenericType &&
                    VerifyGenericTypesAreCompatible(parameterType, realType))
                {
                    result = true;
                }

                if (parameterType.IsArray &&
                    VerifyArrayTypesAreCompatible(parameterType, realType))
                {
                    result = true;
                }

                // Try binding to base types too.
                foreach (Type intefaceType in realType.GetInterfacesAndBase())
                {
                    if (TryBind(parameterType, intefaceType))
                    {
                        result = true;
                    }
                }

                return result;
            }


            /// <summary>
            /// Asserts generic arguments for special attributes : new(), class, struct
            /// </summary>
            /// <param name="genericParameterType"></param>
            /// <param name="realType"></param>
            /// <returns></returns>
            private bool VerifyGenericConstraints(Type genericParameterType, Type realType)
            {
                // Bind constraints to real type 
                // (Some generic parameters appear only as constraints)
                return genericParameterType.GetGenericParameterConstraints()
                    .All(constraint => TryBind(constraint, realType));
            }

            private bool VerifyGenericTypesAreCompatible(Type genericType, Type realType)
            {
                IEnumerable<Type> implementations =
                    realType
                        .GetClosedGenericTypeImplementation
                        (genericType.GetGenericTypeDefinition());

                return
                    implementations.Where(implementation => VerifyGenericImplementationIsCompatible(genericType, implementation))
                        .ToList()
                        .Any();
            }

            private bool VerifyGenericImplementationIsCompatible(Type genericType, Type implementation)
            {
                Type[] implementationArguments = implementation.GetGenericArguments();
                Type[] staticArguments = genericType.GetGenericArguments();

                for (int i = 0; i < implementationArguments.Length; i++)
                {
                    if (!TryBind(staticArguments[i], implementationArguments[i]))
                    {
                        return false;
                    }
                }

                return true;
            }

            /// <summary>
            /// Asserts that the given <paramref name="parameterType"/> and
            /// <paramref name="realType"/> are both compatible in case
            /// that they are arrays.
            /// </summary>
            /// <param name="parameterType"></param>
            /// <param name="realType"></param>
            /// <returns></returns>
            private bool VerifyArrayTypesAreCompatible(Type parameterType, Type realType)
            {
                if (parameterType.IsArray && realType.IsArray)
                {
                    if (parameterType.GetArrayRank() != realType.GetArrayRank())
                    {
                        return false;
                    }

                    if (!TryBind(parameterType.GetElementType(), realType.GetElementType()))
                    {
                        return false;
                    }
                }

                return true;
            }

            public IEnumerable<Type[]> GetGenericArgumentsCandidates()
            {
                IEnumerable<IEnumerable<Type>> product =
                    Combinatorics.CartesianProduct(_genericConstraintsToType.Values);

                foreach (IEnumerable<Type> candidate in product)
{'fraction_non_alphanumeric': 0.08375652671482726, 'fraction_numerical': 0.00014305128388527287, 'mean_word_length': 1.3385181468472989, 'pattern_counts': {'":': 0, '<': 104, '<?xml version=': 0, '>': 110, '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': '1677999', 'n_tokens_mistral': 3110, 'n_tokens_neox': 2879, 'n_words': 714}