<commit_before>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#if NET5_0
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
using osu.Framework.Extensions;
using osu.Framework.Lists;
using osu.Framework.Logging;

namespace osu.Framework.Testing
{
    internal class RoslynTypeReferenceBuilder : ITypeReferenceBuilder
    {
        // The "Attribute" suffix disappears when used via a nuget package, so it is trimmed here.
        private static readonly string exclude_attribute_name = nameof(ExcludeFromDynamicCompileAttribute).Replace(nameof(Attribute), string.Empty);
        private const string jetbrains_annotations_namespace = "JetBrains.Annotations";

        private readonly Logger logger;

        private readonly Dictionary<TypeReference, IReadOnlyCollection<TypeReference>> referenceMap = new Dictionary<TypeReference, IReadOnlyCollection<TypeReference>>();
        private readonly Dictionary<Project, Compilation> compilationCache = new Dictionary<Project, Compilation>();
        private readonly Dictionary<string, SemanticModel> semanticModelCache = new Dictionary<string, SemanticModel>();
        private readonly Dictionary<TypeReference, bool> typeInheritsFromGameCache = new Dictionary<TypeReference, bool>();
        private readonly Dictionary<string, bool> syntaxExclusionMap = new Dictionary<string, bool>();
        private readonly HashSet<string> assembliesContainingReferencedInternalMembers = new HashSet<string>();

        private Solution solution;

        public RoslynTypeReferenceBuilder()
        {
            logger = Logger.GetLogger("dynamic-compilation");
            logger.OutputToListeners = false;
        }

        public async Task Initialise(string solutionFile)
        {
            MSBuildLocator.RegisterDefaults();
            solution = await MSBuildWorkspace.Create().OpenSolutionAsync(solutionFile).ConfigureAwait(false);
        }

        public async Task<IReadOnlyCollection<string>> GetReferencedFiles(Type testType, string changedFile)
        {
            clearCaches();
            updateFile(changedFile);

            await buildReferenceMapAsync(testType, changedFile).ConfigureAwait(false);

            var sources = getTypesFromFile(changedFile).ToArray();
            if (sources.Length == 0)
                throw new NoLinkBetweenTypesException(testType, changedFile);

            return getReferencedFiles(sources, getDirectedGraph());
        }

        public async Task<IReadOnlyCollection<AssemblyReference>> GetReferencedAssemblies(Type testType, string changedFile) => await Task.Run(() =>
        {
            // Todo: This is temporary, and is potentially missing assemblies.

            var assemblies = new HashSet<AssemblyReference>();

            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic))
                addReference(asm, false);
            addReference(typeof(JetBrains.Annotations.NotNullAttribute).Assembly, true);

            return assemblies;

            void addReference(Assembly assembly, bool force)
            {
                if (string.IsNullOrEmpty(assembly.Location))
                    return;

                Type[] loadedTypes = assembly.GetLoadableTypes();

                // JetBrains.Annotations is a special namespace that some libraries define to take advantage of R# annotations.
                // Since internals are exposed to the compiler, these libraries would cause type conflicts and are thus excluded.
                if (!force && loadedTypes.Any(t => t.Namespace == jetbrains_annotations_namespace))
                    return;

                bool containsReferencedInternalMember = assembliesContainingReferencedInternalMembers.Any(i => assembly.FullName?.Contains(i) == true);
                assemblies.Add(new AssemblyReference(assembly, containsReferencedInternalMember));
            }
        }).ConfigureAwait(false);

        public void Reset()
        {
            clearCaches();
            referenceMap.Clear();
        }

        /// <summary>
        /// Builds the reference map, connecting all types to their immediate references. Results are placed inside <see cref="referenceMap"/>.
        /// </summary>
        /// <param name="testType">The test target - the top-most level.</param>
        /// <param name="changedFile">The file that was changed.</param>
        /// <exception cref="InvalidOperationException">If <paramref name="testType"/> could not be retrieved from the solution.</exception>
        private async Task buildReferenceMapAsync(Type testType, string changedFile)
        {
            // We want to find a graph of types from the testType symbol (P) to all the types which it references recursively.
            //
            //                            P
            //                          /  \
            //                         /    \
            //                        /      \
            //                      C1       C2 ---
            //                    /   \      |    /
            //                  C3    C4    C5   /
            //                          \  /    /
            //                           C6 ---
            //
            // The reference map is a key-value pairing of all types to their immediate references. A directed graph can be built by traversing through types.
            //
            // P -> { C1, C2 }
            // C1 -> { C3, C4 }
            // C2 -> { C5, C6 }
            // C3 -> { }
            // C4 -> { C6 }
            // C5 -> { C6 }
            // C6 -> { C2 }

            logger.Add("Building reference map...");

            var compiledTestProject = await compileProjectAsync(findTestProject()).ConfigureAwait(false);
            var compiledTestType = compiledTestProject.GetTypeByMetadataName(testType.FullName);

            if (compiledTestType == null)
                throw new InvalidOperationException("Failed to retrieve test type from the solution.");

            if (referenceMap.Count > 0)
            {
                logger.Add("Attempting to use cache...");

                // We already have some references, so we can do a partial re-process of the map for only the changed file.
                var oldTypes = getTypesFromFile(changedFile).ToArray();

                foreach (var t in oldTypes)
                {
                    referenceMap.Remove(t);
                    typeInheritsFromGameCache.Remove(t);
                }

                foreach (var t in oldTypes)
                {
                    string typePath = t.Symbol.Locations.First().SourceTree?.FilePath;

                    // The type we have is on an old compilation, we need to re-retrieve it on the new one.
                    var project = getProjectFromFile(typePath);

                    if (project == null)
                    {
                        logger.Add("File has been renamed. Rebuilding reference map from scratch...");
                        Reset();
                        break;
                    }

                    var compilation = await compileProjectAsync(project).ConfigureAwait(false);
                    var syntaxTree = compilation.SyntaxTrees.Single(tree => tree.FilePath == typePath);
                    var semanticModel = await getSemanticModelAsync(syntaxTree).ConfigureAwait(false);
                    var referencedTypes = await getReferencedTypesAsync(semanticModel).ConfigureAwait(false);

                    referenceMap[TypeReference.FromSymbol(t.Symbol)] = referencedTypes;

                    foreach (var referenced in referencedTypes)
                        await buildReferenceMapRecursiveAsync(referenced).ConfigureAwait(false);
                }
            }

            if (referenceMap.Count == 0)
            {
                // We have no cache available, so we must rebuild the whole map.
                await buildReferenceMapRecursiveAsync(TypeReference.FromSymbol(compiledTestType)).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// Builds the reference map starting from a root type reference, connecting all types to their immediate references. Results are placed inside <see cref="referenceMap"/>.
        /// </summary>
        /// <remarks>
        /// This should not be used by itself. Use <see cref="buildReferenceMapAsync"/> instead.
        /// </remarks>
        /// <param name="rootReference">The root, where the map should start being build from.</param>
        private async Task buildReferenceMapRecursiveAsync(TypeReference rootReference)
        {
            var searchQueue = new Queue<TypeReference>();
            searchQueue.Enqueue(rootReference);

            while (searchQueue.Count > 0)
            {
                var toCheck = searchQueue.Dequeue();
                var referencedTypes = await getReferencedTypesAsync(toCheck).ConfigureAwait(false);

                referenceMap[toCheck] = referencedTypes;

                foreach (var referenced in referencedTypes)
                {
                    // We don't want to cycle over types that have already been explored.
                    if (!referenceMap.ContainsKey(referenced))
                    {
                        // Used for de-duping, so it must be added to the dictionary immediately.
                        referenceMap[referenced] = null;
                        searchQueue.Enqueue(referenced);
                    }
                }
            }
        }

        /// <summary>
        /// Retrieves all <see cref="TypeReference"/>s referenced by a given <see cref="TypeReference"/>, across all symbol sources.
        /// </summary>
        /// <param name="typeReference">The target <see cref="TypeReference"/>.</param>
        /// <returns>All <see cref="TypeReference"/>s referenced to across all symbol sources by <paramref name="typeReference"/>.</returns>
        private async Task<HashSet<TypeReference>> getReferencedTypesAsync(TypeReference typeReference)
        {
            var result = new HashSet<TypeReference>();

            foreach (var reference in typeReference.Symbol.DeclaringSyntaxReferences)
            {
                var semanticModel = await getSemanticModelAsync(reference.SyntaxTree).ConfigureAwait(false);
                var referencedTypes = await getReferencedTypesAsync(semanticModel).ConfigureAwait(false);

                foreach (var type in referencedTypes)
                    result.Add(type);
            }

            return result;
        }

        /// <summary>
        /// Retrieves all <see cref="TypeReference"/>s referenced by a given <see cref="SemanticModel"/>.
        /// </summary>
        /// <param name="semanticModel">The target <see cref="SemanticModel"/>.</param>
        /// <returns>All <see cref="TypeReference"/>s referenced by <paramref name="semanticModel"/>.</returns>
        private async Task<HashSet<TypeReference>> getReferencedTypesAsync(SemanticModel semanticModel)
        {
            var result = new HashSet<TypeReference>();

            var root = await semanticModel.SyntaxTree.GetRootAsync().ConfigureAwait(false);
            var descendantNodes = root.DescendantNodes(n =>
            {
                var kind = n.Kind();

                // Ignored:
                // - Entire using lines.
                // - Namespace names (not entire namespaces).
                // - Entire static classes.
                // - Variable declarators (names of variables).
                // - The single IdentifierName child of an assignment expression (variable name), below.
                // - The single IdentifierName child of an argument syntax (variable name), below.
                // - The name of namespace declarations.
                // - Name-colon syntaxes.
                // - The expression of invocations. Static classes are explicitly disallowed so the target type of an invocation must be available elsewhere in the syntax tree.

                return kind != SyntaxKind.UsingDirective
                       && kind != SyntaxKind.NamespaceKeyword
                       && (kind != SyntaxKind.ClassDeclaration || ((ClassDeclarationSyntax)n).Modifiers.All(m => m.Kind() != SyntaxKind.StaticKeyword))
                       && kind != SyntaxKind.VariableDeclarator
                       && (kind != SyntaxKind.QualifiedName || !(n.Parent is NamespaceDeclarationSyntax))
                       && kind != SyntaxKind.NameColon
                       && (!(n.Parent is InvocationExpressionSyntax invocation) || n != invocation.Expression);
            });

            // This hashset is used to prevent re-exploring syntaxes with the same name.
            // Todo: This can be used across all files, but care needs to be taken for redefined types (via using X = y), using the same-named type from a different namespace, or via type hiding.
            var seenTypes = new HashSet<string>();

            // Find all the named type symbols in the syntax tree, and mark + recursively iterate through them.
            foreach (var node in descendantNodes)
            {
                if (node.Kind() == SyntaxKind.IdentifierName)
                {
                    // Ignore the variable name of assignment expressions.
                    if (node.Parent is AssignmentExpressionSyntax)
                        continue;

                    // Ignore the variable name of arguments.
                    if (node.Parent is ArgumentSyntax)
                        continue;
                }

                switch (node.Kind())
                {
                    case SyntaxKind.GenericName:
                    case SyntaxKind.IdentifierName:
                    {
                        string syntaxName = node.ToString();

                        if (seenTypes.Contains(syntaxName))
                            continue;

                        if (!tryNode(node, out var symbol))
                            continue;
<commit_msg>Fix single identifier name invocation<commit_after>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#if NET5_0
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
using osu.Framework.Extensions;
using osu.Framework.Lists;
using osu.Framework.Logging;

namespace osu.Framework.Testing
{
    internal class RoslynTypeReferenceBuilder : ITypeReferenceBuilder
    {
        // The "Attribute" suffix disappears when used via a nuget package, so it is trimmed here.
        private static readonly string exclude_attribute_name = nameof(ExcludeFromDynamicCompileAttribute).Replace(nameof(Attribute), string.Empty);
        private const string jetbrains_annotations_namespace = "JetBrains.Annotations";

        private readonly Logger logger;

        private readonly Dictionary<TypeReference, IReadOnlyCollection<TypeReference>> referenceMap = new Dictionary<TypeReference, IReadOnlyCollection<TypeReference>>();
        private readonly Dictionary<Project, Compilation> compilationCache = new Dictionary<Project, Compilation>();
        private readonly Dictionary<string, SemanticModel> semanticModelCache = new Dictionary<string, SemanticModel>();
        private readonly Dictionary<TypeReference, bool> typeInheritsFromGameCache = new Dictionary<TypeReference, bool>();
        private readonly Dictionary<string, bool> syntaxExclusionMap = new Dictionary<string, bool>();
        private readonly HashSet<string> assembliesContainingReferencedInternalMembers = new HashSet<string>();

        private Solution solution;

        public RoslynTypeReferenceBuilder()
        {
            logger = Logger.GetLogger("dynamic-compilation");
            logger.OutputToListeners = false;
        }

        public async Task Initialise(string solutionFile)
        {
            MSBuildLocator.RegisterDefaults();
            solution = await MSBuildWorkspace.Create().OpenSolutionAsync(solutionFile).ConfigureAwait(false);
        }

        public async Task<IReadOnlyCollection<string>> GetReferencedFiles(Type testType, string changedFile)
        {
            clearCaches();
            updateFile(changedFile);

            await buildReferenceMapAsync(testType, changedFile).ConfigureAwait(false);

            var sources = getTypesFromFile(changedFile).ToArray();
            if (sources.Length == 0)
                throw new NoLinkBetweenTypesException(testType, changedFile);

            return getReferencedFiles(sources, getDirectedGraph());
        }

        public async Task<IReadOnlyCollection<AssemblyReference>> GetReferencedAssemblies(Type testType, string changedFile) => await Task.Run(() =>
        {
            // Todo: This is temporary, and is potentially missing assemblies.

            var assemblies = new HashSet<AssemblyReference>();

            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic))
                addReference(asm, false);
            addReference(typeof(JetBrains.Annotations.NotNullAttribute).Assembly, true);

            return assemblies;

            void addReference(Assembly assembly, bool force)
            {
                if (string.IsNullOrEmpty(assembly.Location))
                    return;

                Type[] loadedTypes = assembly.GetLoadableTypes();

                // JetBrains.Annotations is a special namespace that some libraries define to take advantage of R# annotations.
                // Since internals are exposed to the compiler, these libraries would cause type conflicts and are thus excluded.
                if (!force && loadedTypes.Any(t => t.Namespace == jetbrains_annotations_namespace))
                    return;

                bool containsReferencedInternalMember = assembliesContainingReferencedInternalMembers.Any(i => assembly.FullName?.Contains(i) == true);
                assemblies.Add(new AssemblyReference(assembly, containsReferencedInternalMember));
            }
        }).ConfigureAwait(false);

        public void Reset()
        {
            clearCaches();
            referenceMap.Clear();
        }

        /// <summary>
        /// Builds the reference map, connecting all types to their immediate references. Results are placed inside <see cref="referenceMap"/>.
        /// </summary>
        /// <param name="testType">The test target - the top-most level.</param>
        /// <param name="changedFile">The file that was changed.</param>
        /// <exception cref="InvalidOperationException">If <paramref name="testType"/> could not be retrieved from the solution.</exception>
        private async Task buildReferenceMapAsync(Type testType, string changedFile)
        {
            // We want to find a graph of types from the testType symbol (P) to all the types which it references recursively.
            //
            //                            P
            //                          /  \
            //                         /    \
            //                        /      \
            //                      C1       C2 ---
            //                    /   \      |    /
            //                  C3    C4    C5   /
            //                          \  /    /
            //                           C6 ---
            //
            // The reference map is a key-value pairing of all types to their immediate references. A directed graph can be built by traversing through types.
            //
            // P -> { C1, C2 }
            // C1 -> { C3, C4 }
            // C2 -> { C5, C6 }
            // C3 -> { }
            // C4 -> { C6 }
            // C5 -> { C6 }
            // C6 -> { C2 }

            logger.Add("Building reference map...");

            var compiledTestProject = await compileProjectAsync(findTestProject()).ConfigureAwait(false);
            var compiledTestType = compiledTestProject.GetTypeByMetadataName(testType.FullName);

            if (compiledTestType == null)
                throw new InvalidOperationException("Failed to retrieve test type from the solution.");

            if (referenceMap.Count > 0)
            {
                logger.Add("Attempting to use cache...");

                // We already have some references, so we can do a partial re-process of the map for only the changed file.
                var oldTypes = getTypesFromFile(changedFile).ToArray();

                foreach (var t in oldTypes)
                {
                    referenceMap.Remove(t);
                    typeInheritsFromGameCache.Remove(t);
                }

                foreach (var t in oldTypes)
                {
                    string typePath = t.Symbol.Locations.First().SourceTree?.FilePath;

                    // The type we have is on an old compilation, we need to re-retrieve it on the new one.
                    var project = getProjectFromFile(typePath);

                    if (project == null)
                    {
                        logger.Add("File has been renamed. Rebuilding reference map from scratch...");
                        Reset();
                        break;
                    }

                    var compilation = await compileProjectAsync(project).ConfigureAwait(false);
                    var syntaxTree = compilation.SyntaxTrees.Single(tree => tree.FilePath == typePath);
                    var semanticModel = await getSemanticModelAsync(syntaxTree).ConfigureAwait(false);
                    var referencedTypes = await getReferencedTypesAsync(semanticModel).ConfigureAwait(false);

                    referenceMap[TypeReference.FromSymbol(t.Symbol)] = referencedTypes;

                    foreach (var referenced in referencedTypes)
                        await buildReferenceMapRecursiveAsync(referenced).ConfigureAwait(false);
                }
            }

            if (referenceMap.Count == 0)
            {
                // We have no cache available, so we must rebuild the whole map.
                await buildReferenceMapRecursiveAsync(TypeReference.FromSymbol(compiledTestType)).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// Builds the reference map starting from a root type reference, connecting all types to their immediate references. Results are placed inside <see cref="referenceMap"/>.
        /// </summary>
        /// <remarks>
        /// This should not be used by itself. Use <see cref="buildReferenceMapAsync"/> instead.
        /// </remarks>
        /// <param name="rootReference">The root, where the map should start being build from.</param>
        private async Task buildReferenceMapRecursiveAsync(TypeReference rootReference)
        {
            var searchQueue = new Queue<TypeReference>();
            searchQueue.Enqueue(rootReference);

            while (searchQueue.Count > 0)
            {
                var toCheck = searchQueue.Dequeue();
                var referencedTypes = await getReferencedTypesAsync(toCheck).ConfigureAwait(false);

                referenceMap[toCheck] = referencedTypes;

                foreach (var referenced in referencedTypes)
                {
                    // We don't want to cycle over types that have already been explored.
                    if (!referenceMap.ContainsKey(referenced))
                    {
                        // Used for de-duping, so it must be added to the dictionary immediately.
                        referenceMap[referenced] = null;
                        searchQueue.Enqueue(referenced);
                    }
                }
            }
        }

        /// <summary>
        /// Retrieves all <see cref="TypeReference"/>s referenced by a given <see cref="TypeReference"/>, across all symbol sources.
        /// </summary>
        /// <param name="typeReference">The target <see cref="TypeReference"/>.</param>
        /// <returns>All <see cref="TypeReference"/>s referenced to across all symbol sources by <paramref name="typeReference"/>.</returns>
        private async Task<HashSet<TypeReference>> getReferencedTypesAsync(TypeReference typeReference)
        {
            var result = new HashSet<TypeReference>();

            foreach (var reference in typeReference.Symbol.DeclaringSyntaxReferences)
            {
                var semanticModel = await getSemanticModelAsync(reference.SyntaxTree).ConfigureAwait(false);
                var referencedTypes = await getReferencedTypesAsync(semanticModel).ConfigureAwait(false);

                foreach (var type in referencedTypes)
                    result.Add(type);
            }

            return result;
        }

        /// <summary>
        /// Retrieves all <see cref="TypeReference"/>s referenced by a given <see cref="SemanticModel"/>.
        /// </summary>
        /// <param name="semanticModel">The target <see cref="SemanticModel"/>.</param>
        /// <returns>All <see cref="TypeReference"/>s referenced by <paramref name="semanticModel"/>.</returns>
        private async Task<HashSet<TypeReference>> getReferencedTypesAsync(SemanticModel semanticModel)
        {
            var result = new HashSet<TypeReference>();

            var root = await semanticModel.SyntaxTree.GetRootAsync().ConfigureAwait(false);
            var descendantNodes = root.DescendantNodes(n =>
            {
                var kind = n.Kind();

                // Ignored:
                // - Entire using lines.
                // - Namespace names (not entire namespaces).
                // - Entire static classes.
                // - Variable declarators (names of variables).
                // - The single IdentifierName child of an assignment expression (variable name), below.
                // - The single IdentifierName child of an argument syntax (variable name), below.
                // - The name of namespace declarations.
                // - Name-colon syntaxes.
                // - The expression of invocation expressions. Static classes are explicitly disallowed so the target type of an invocation must be available elsewhere in the syntax tree.

                return kind != SyntaxKind.UsingDirective
                       && kind != SyntaxKind.NamespaceKeyword
                       && (kind != SyntaxKind.ClassDeclaration || ((ClassDeclarationSyntax)n).Modifiers.All(m => m.Kind() != SyntaxKind.StaticKeyword))
                       && kind != SyntaxKind.VariableDeclarator
                       && (kind != SyntaxKind.QualifiedName || !(n.Parent is NamespaceDeclarationSyntax))
                       && kind != SyntaxKind.NameColon
                       && (n.Parent?.Kind() != SyntaxKind.InvocationExpression || n != ((InvocationExpressionSyntax)n.Parent).Expression);
            });

            // This hashset is used to prevent re-exploring syntaxes with the same name.
            // Todo: This can be used across all files, but care needs to be taken for redefined types (via using X = y), using the same-named type from a different namespace, or via type hiding.
            var seenTypes = new HashSet<string>();

            // Find all the named type symbols in the syntax tree, and mark + recursively iterate through them.
            foreach (var node in descendantNodes)
            {
                if (node.Kind() == SyntaxKind.IdentifierName)
                {
                    // Ignore the variable name of assignment expressions.
                    if (node.Parent is AssignmentExpressionSyntax)
                        continue;

                    // Ignore the variable name of arguments.
                    if (node.Parent is ArgumentSyntax)
                        continue;

                    // Ignore a single identifier name expression of an invocation expression (e.g. IdentifierName()).
                    if (node.Parent.Kind() == SyntaxKind.InvocationExpression)
                        continue;
                }

                switch (node.Kind())
                {
                    case SyntaxKind.GenericName:
                    case SyntaxKind.IdentifierName:
                    {
                        string syntaxName = node.ToString();

                        if (seenTypes.Contains(syntaxName))
                            continue;

                        if (!tryNode(node, out var symbol))
                            continue;

{'fraction_non_alphanumeric': 0.08706116975552729, 'fraction_numerical': 0.0018567548052126671, 'mean_word_length': 1.8455141375599255, 'pattern_counts': {'":': 0, '<': 141, '<?xml version=': 0, '>': 175, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7647', 'n_tokens_mistral': 6849, 'n_tokens_neox': 6418, 'n_words': 1945}