/// <summary>
        /// Runs the transformations needed to produce a valid compilation unit.
        /// </summary>
        public void Run(bool persistGeneratedCode, bool optimizeCode)
        {
#if TIMING
            Stopwatch sw = new Stopwatch();
            sw.Start();
#endif
            // side-effect: creates this.compilation
            CreateCompilation(persistGeneratedCode, optimizeCode);

            foreach (var rtTP in this.runtimeTypeParameters)
            {
                AddAssemblyReferencesNeededFor(rtTP);
            }

            var d = new Dictionary <ISymbol, SyntaxNode>();
            var i = 0;
            UpdateDictionary(d, FindSymbol("Key"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("Value"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("Input"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("Output"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("Context"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("Functions"), this.runtimeTypeParameters.ElementAt(i++));
            UpdateDictionary(d, FindSymbol("IFasterKV"), this.runtimeTypeParameters.ElementAt(i++));

            var pass1 = new TypeReplacer(this.compilation, d);
            var pass2 = new NamespaceReplacer(this.compilation);

            var FASTDotCoreNamespaceName = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("FASTER"), SyntaxFactory.IdentifierName("core"));
            var usingFASTDotCore         = SyntaxFactory.UsingDirective(FASTDotCoreNamespaceName);

            foreach (var t in compilation.SyntaxTrees)
            {
                var oldTree = t;
                var oldNode = t.GetRoot();
                var newNode = pass1.Visit(oldNode);
                newNode = pass2.Visit(newNode);

                var newRoot = oldTree.GetRoot().ReplaceNode(oldNode, newNode);
                var newTree = oldTree
                              .WithRootAndOptions(newRoot, CSharpParseOptions.Default)
                ;
                var compilationSyntax = (CompilationUnitSyntax)newTree.GetRoot();
                compilationSyntax = compilationSyntax.AddUsings(usingFASTDotCore);
                newTree           = newTree
                                    .WithRootAndOptions(compilationSyntax, CSharpParseOptions.Default);

                compilation = compilation.ReplaceSyntaxTree(oldTree, newTree);
            }

#if TIMING
            sw.Stop();
            System.Diagnostics.Debug.WriteLine("Time to run the FasterHashTable compiler: {0}ms", sw.ElapsedMilliseconds);
            using (var fileStream = new StreamWriter("foo.txt", true))
            {
                fileStream.WriteLine("Time to run the FasterHashTable compiler: {0}ms", sw.ElapsedMilliseconds);
            }
#endif
        }
        /// <summary>
        /// Runs the transformations needed to produce a valid compilation unit.
        /// </summary>
        public void Run(bool persistGeneratedCode, bool optimizeCode)
        {
#if TIMING
            Stopwatch sw = new Stopwatch();
            sw.Start();
#endif

            var userKeyTypeName     = this.typeMapper.CSharpNameFor(typeof(TKey));
            var userValueTypeName   = this.typeMapper.CSharpNameFor(typeof(TValue));
            var userInputTypeName   = this.typeMapper.CSharpNameFor(typeof(TInput));
            var userOutputTypeName  = this.typeMapper.CSharpNameFor(typeof(TOutput));
            var userContextTypeName = this.typeMapper.CSharpNameFor(typeof(TContext));

            var internalKeyTypeName     = userKeyTypeName;
            var internalValueTypeName   = userValueTypeName;
            var internalInputTypeName   = userInputTypeName;
            var internalOutputTypeName  = userOutputTypeName;
            var internalContextTypeName = userContextTypeName;

            #region Things needed before creating the compilation

            var    preprocessorSymbols  = new List <string>();
            string internalWrappedTypes = "";

            if (IsBlittable <TKey>())
            {
                var tKeyType = typeof(TKey);
                preprocessorSymbols.Add("BLIT_KEY");
                if (tKeyType.IsGenericType)
                {
                    preprocessorSymbols.Add("GENERIC_BLIT_KEY");
                    internalKeyTypeName   = "Key_" + String.Join("_", tKeyType.GenericTypeArguments.Select(t => t.GetCSharpSourceSyntax().CleanUpIdentifierName()));
                    internalWrappedTypes += GenerateInternalWrappedType <TKey>(internalKeyTypeName);
                }
            }

            if (this.treatValueAsAtomic)
            {
                preprocessorSymbols.Add("VALUE_ATOMIC");
            }
            if (IsBlittable <TValue>())
            {
                var tValueType = typeof(TValue);
                preprocessorSymbols.Add("BLIT_VALUE");
                if (tValueType.IsGenericType)
                {
                    preprocessorSymbols.Add("GENERIC_BLIT_VALUE");
                    internalValueTypeName = "Value_" + String.Join("_", tValueType.GenericTypeArguments.Select(t => t.GetCSharpSourceSyntax().CleanUpIdentifierName()));
                    internalWrappedTypes += GenerateInternalWrappedType <TValue>(internalValueTypeName);
                }
            }
            if (IsBlittable <TInput>())
            {
                var tInputType = typeof(TInput);
                preprocessorSymbols.Add("BLIT_INPUT");
                if (tInputType.IsGenericType)
                {
                    preprocessorSymbols.Add("GENERIC_BLIT_INPUT");
                    internalInputTypeName = "Input_" + String.Join("_", tInputType.GenericTypeArguments.Select(t => t.GetCSharpSourceSyntax().CleanUpIdentifierName()));
                    internalWrappedTypes += GenerateInternalWrappedType <TInput>(internalInputTypeName);
                }
            }

            if (IsBlittable <TOutput>())
            {
                var tOutputType = typeof(TOutput);
                preprocessorSymbols.Add("BLIT_OUTPUT");
                if (tOutputType.IsGenericType)
                {
                    preprocessorSymbols.Add("GENERIC_BLIT_OUTPUT");
                    internalOutputTypeName = "Output_" + String.Join("_", tOutputType.GenericTypeArguments.Select(t => t.GetCSharpSourceSyntax().CleanUpIdentifierName()));
                    internalWrappedTypes  += GenerateInternalWrappedType <TOutput>(internalOutputTypeName);
                }
            }

            if (IsBlittable <TContext>())
            {
                var tContextType = typeof(TContext);
                preprocessorSymbols.Add("BLIT_CONTEXT");
                if (tContextType.IsGenericType)
                {
                    preprocessorSymbols.Add("GENERIC_BLIT_CONTEXT");
                    internalContextTypeName = "Context_" + String.Join("_", tContextType.GenericTypeArguments.Select(t => t.GetCSharpSourceSyntax().CleanUpIdentifierName()));
                    internalWrappedTypes   += GenerateInternalWrappedType <TContext>(internalContextTypeName);
                }
            }

            #endregion

            // side-effect: creates this.compilation
            CreateCompilation(persistGeneratedCode, optimizeCode, preprocessorSymbols: preprocessorSymbols);

            foreach (var rtTP in this.runtimeTypeParameters)
            {
                AddAssemblyReferencesNeededFor(rtTP);
            }

            var d = new Dictionary <string, IDictionary <ISymbol, SyntaxNode> >();

            var userDictionary = new Dictionary <ISymbol, SyntaxNode>
            {
                { FindSymbol("MixedKey"), SyntaxFactory.ParseTypeName(userKeyTypeName) },
                { FindSymbol("MixedValue"), SyntaxFactory.ParseTypeName(userValueTypeName) },
                { FindSymbol("MixedInput"), SyntaxFactory.ParseTypeName(userInputTypeName) },
                { FindSymbol("MixedOutput"), SyntaxFactory.ParseTypeName(userOutputTypeName) },
                { FindSymbol("MixedContext"), SyntaxFactory.ParseTypeName(userContextTypeName) },
                { FindSymbol("MixedUserFunctions"), SyntaxFactory.ParseTypeName(this.typeMapper.CSharpNameFor(typeof(TFunctions))) }
            };

            d.Add("user", userDictionary);

            var internalDictionary = new Dictionary <ISymbol, SyntaxNode>
            {
                { FindSymbol("MixedKey"), SyntaxFactory.ParseTypeName(internalKeyTypeName) },
                { FindSymbol("MixedValue"), SyntaxFactory.ParseTypeName(internalValueTypeName) },
                { FindSymbol("MixedInput"), SyntaxFactory.ParseTypeName(internalInputTypeName) },
                { FindSymbol("MixedOutput"), SyntaxFactory.ParseTypeName(internalOutputTypeName) },
                { FindSymbol("MixedContext"), SyntaxFactory.ParseTypeName(internalContextTypeName) },
                { FindSymbol("MixedUserFunctions"), SyntaxFactory.ParseTypeName(this.typeMapper.CSharpNameFor(typeof(TFunctions))) }
            };

            d.Add("internal", internalDictionary);

            var pass1 = new MultiDictionaryTypeReplacer(this.compilation, d);
            var pass2 = new NamespaceReplacer(this.compilation);

            var FASTDotCoreNamespaceName = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("FASTER"), SyntaxFactory.IdentifierName("core"));
            var usingFASTDotCore         = SyntaxFactory.UsingDirective(FASTDotCoreNamespaceName);

            foreach (var t in compilation.SyntaxTrees)
            {
                var oldTree = t;
                var oldNode = t.GetRoot();
                var newNode = pass1.Visit(oldNode);
                newNode = pass2.Visit(newNode);

                var newRoot = oldTree.GetRoot().ReplaceNode(oldNode, newNode);
                var newTree = oldTree
                              .WithRootAndOptions(newRoot, CSharpParseOptions.Default)
                ;
                var compilationSyntax = (CompilationUnitSyntax)newTree.GetRoot();
                compilationSyntax = compilationSyntax.AddUsings(usingFASTDotCore);
                newTree           = newTree
                                    .WithRootAndOptions(compilationSyntax, CSharpParseOptions.Default);

                compilation = compilation.ReplaceSyntaxTree(oldTree, newTree);
            }

            #region Create new source files from scratch (instead of from a template): it does *not* get transformed

            if (!String.IsNullOrWhiteSpace(internalWrappedTypes))
            {
                internalWrappedTypes =
                    "using System;\r\n" +
                    "using System.Runtime.CompilerServices;\r\n" +
                    "using System.Runtime.InteropServices;\r\n" +
                    $"namespace FASTER.core.Codegen_{this.compilation.AssemblyName}\r\n" +
                    "{\r\n" +
                    internalWrappedTypes +
                    "}\r\n"
                ;

                this.AddSource(internalWrappedTypes, "InternalWrappedTypes");
            }

            #endregion



#if TIMING
            sw.Stop();
            System.Diagnostics.Debug.WriteLine("Time to run the FasterHashTable compiler: {0}ms", sw.ElapsedMilliseconds);
            using (var fileStream = new StreamWriter("foo.txt", true))
            {
                fileStream.WriteLine("Time to run the FasterHashTable compiler: {0}ms", sw.ElapsedMilliseconds);
            }
#endif
        }