Example #1
0
        public static List<string> GenerateCode(Specification S, G25.CG.Shared.CGdata cgd)
        {
            // get filename, list of generated filenames
            List<string> generatedFiles = new List<string>();
            string sourceFilename = S.GetOutputPath(G25.CG.C.Source.GetRawSourceFilename(S));
            generatedFiles.Add(sourceFilename);

            // get StringBuilder where all generated code goes
            StringBuilder SB = new StringBuilder();

            // output license, copyright
            G25.CG.Shared.Util.WriteCopyright(SB, S);
            G25.CG.Shared.Util.WriteLicense(SB, S);

            { // #includes
                SB.AppendLine("#include <stdio.h>");
                if (cgd.GetFeedback(G25.CG.Shared.Main.NEED_TIME) == "true")
                    SB.AppendLine("#include <time.h> /* used to seed random generator */");
                SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.Header.GetRawHeaderFilename(S)) + "\"");
            }

            GenerateTables(S, cgd, SB);

            // the list of names of smv types
            G25.CG.C.SMV.WriteSMVtypenames(SB, S, cgd);

            // write constant definitions
            G25.CG.C.Constants.WriteDefinitions(SB, S, cgd);

            // set to zero / copy floats
            cgd.m_cog.EmitTemplate(SB, "float_zero_copy_def", "S=", S, "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);

            if (S.m_gmvCodeGeneration == GMV_CODE.RUNTIME)
            {
                cgd.m_cog.EmitTemplate(SB, "runtimeGpTablesDefs", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "bitmapGp", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "runtimeGpTable", "S=", S);
                foreach (G25.FloatType FT in S.m_floatTypes)
                {
                    cgd.m_cog.EmitTemplate(SB, "runtimeComputeGp", "S=", S, "FT=", FT);
                }
                cgd.m_cog.EmitTemplate(SB, "runtimeGpInitTables", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "runtimeGpFreeTables", "S=", S);
            }

            // write compress source code
            WriteCompressSource(SB, S, cgd);

            cgd.m_cog.EmitTemplate(SB, "swapPointer");

            { // write toString
                bool def = true;
                G25.CG.C.ToString.WriteToString(SB, S, cgd, def);
            }

            //SB.AppendLine("/* def SB: */");
            SB.Append(cgd.m_defSB);

            // write all to file
            G25.CG.Shared.Util.WriteFile(sourceFilename, SB.ToString());

            return generatedFiles;
        }
Example #2
0
        /// <summary>
        /// Generates a header file named S.m_namespace.h.
        /// </summary>
        /// <param name="S">Specification of algebra.</param>
        /// <param name="cgd">Intermediate data for code generation. Also contains plugins and cog.</param>
        /// <returns>a list of filenames which were generated (full path).</returns>
        public static List<string> GenerateCode(Specification S, G25.CG.Shared.CGdata cgd)
        {
            // get filename, list of generated filenames
            List<string> generatedFiles = new List<string>();
            string headerFilename = S.GetOutputPath(G25.CG.C.Header.GetRawHeaderFilename(S));
            generatedFiles.Add(headerFilename);

            // get StringBuilder where all generated code goes
            StringBuilder SB = new StringBuilder();

            // output license, copyright
            G25.CG.Shared.Util.WriteCopyright(SB, S);
            G25.CG.Shared.Util.WriteLicense(SB, S);

            // write main documentation
            WriteDoxyMainPage(SB, S, cgd);

            // open include guard
            G25.CG.Shared.Util.WriteOpenIncludeGuard(SB, GetRawHeaderFilename(S));

            { // #includes
                SB.AppendLine("");
                SB.AppendLine("#include <stdio.h>");
                SB.AppendLine("#include <stdlib.h>");
                SB.AppendLine("#include <string.h>");
                SB.AppendLine("#include <math.h>");
                if (cgd.GetFeedback(G25.CG.C.MainGenerator.MERSENNE_TWISTER) == "true")
                    SB.AppendLine("#include \"" + S.m_namespace + "_mt.h\"");
            }

            { // basic info

                {    // #define GROUP_ and GRADE_
                    SB.AppendLine("");

                    // group
                    int[] gradeBitmap = new int[S.m_dimension + 1];
                    for (int i = 0; i < S.m_GMV.NbGroups; i++)
                    {
                        gradeBitmap[S.m_GMV.Group(i)[0].Grade()] |= 1 << i;

                        SB.Append("/* group: ");
                        for (int j = 0; j < S.m_GMV.Group(i).Length; j++)
                        {
                            if (j > 0) SB.Append(", ");
                            SB.Append(S.m_GMV.Group(i)[j].ToString(S.m_basisVectorNames));
                        }
                        SB.AppendLine("*/");
                        SB.AppendLine("#define GROUP_" + i + " " + (1 << i));
                    }

                    // grade
                    for (int i = 0; i <= S.m_dimension; i++)
                        SB.AppendLine("#define GRADE_" + i + " " + gradeBitmap[i]);
                }

                cgd.m_cog.EmitTemplate(SB, "basicInfo", "S=", S);

                if (S.m_gmvCodeGeneration == GMV_CODE.RUNTIME)
                    cgd.m_cog.EmitTemplate(SB, "runtimeGpTablesHeader", "S=", S);
            }

            // #define for all specialized MV types
            if (S.m_reportUsage)
                G25.CG.C.SMV.WriteSMVtypeConstants(SB, S, cgd);

            // write structs for all GMV (all float types)
            G25.CG.C.GMV.WriteGMVstructs(SB, S, cgd);

            // write structs for all SMVs (all float types)
            G25.CG.C.SMV.WriteSMVstructs(SB, S, cgd);

            // write structs for all GOM (all float types)
            if (S.m_GOM != null)
                G25.CG.C.GOM.WriteGOMstructs(SB, S, cgd);

            // write structs for all SOMs (all float types)
            G25.CG.C.SOM.WriteSOMstructs(SB, S, cgd);

            { // write toString
                bool def = false;
                G25.CG.C.ToString.WriteToString(SB, S, cgd, def);
            }

            // write constant declarations
            G25.CG.C.Constants.WriteDeclarations(SB, S, cgd);

            // set to zero / copy floats
            cgd.m_cog.EmitTemplate(SB, "float_zero_copy_decl", "S=", S, "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);

            //SB.AppendLine("/* decl SB: */");
            SB.Append(cgd.m_declSB);

            //SB.AppendLine("/* inline def SB: */");
            SB.Append(cgd.m_inlineDefSB);

            // parser declarations:
            if (S.m_parserType == PARSER.ANTLR)
                cgd.m_cog.EmitTemplate(SB, "ANTLRparserHeader", "S=", S, "FT=", S.m_floatTypes[0]);
            else if (S.m_parserType == PARSER.BUILTIN)
                cgd.m_cog.EmitTemplate(SB, "CustomParserHeader", "S=", S, "FT=", S.m_floatTypes[0]);

            // close include guard
            G25.CG.Shared.Util.WriteCloseIncludeGuard(SB, GetRawHeaderFilename(S));

            // write all to file
            G25.CG.Shared.Util.WriteFile(headerFilename, SB.ToString());

            return generatedFiles;
        }
Example #3
0
        /// <summary>
        /// Generates a header file named S.m_namespace.h.
        /// </summary>
        /// <param name="S">Specification of algebra.</param>
        /// <param name="cgd">Intermediate data for code generation. Also contains plugins and cog.</param>
        /// <returns>a list of filenames which were generated (full path).</returns>
        public static List<string> GenerateCode(Specification S, G25.CG.Shared.CGdata cgd)
        {
            // get filename, list of generated filenames
            List<string> generatedFiles = new List<string>();
            string headerFilename = S.GetOutputPath(GetRawHeaderFilename(S));
            generatedFiles.Add(headerFilename);

            // get StringBuilder where all generated code goes
            StringBuilder SB = new StringBuilder();

            // output license, copyright
            G25.CG.Shared.Util.WriteCopyright(SB, S);
            G25.CG.Shared.Util.WriteLicense(SB, S);

            // write main documentation
            WriteDoxyMainPage(SB, S, cgd);

            // open include guard
            G25.CG.Shared.Util.WriteOpenIncludeGuard(SB, GetRawHeaderFilename(S));

            { // #includes
                SB.AppendLine("");
                SB.AppendLine("#include <stdio.h>");
                SB.AppendLine("#include <stdlib.h>");
                SB.AppendLine("#include <string.h>");
                SB.AppendLine("#include <math.h>");
                SB.AppendLine("#include <string>");
                if (cgd.GetFeedback(G25.CG.Shared.Main.NEED_TIME) == "true")
                    SB.AppendLine("#include <time.h> /* used to seed random generator */");
                if (cgd.GetFeedback(G25.CG.Shared.Main.MERSENNE_TWISTER) == "true")
                    SB.AppendLine("#include \"" + S.m_namespace + "_mt.h\"");
                if (S.m_reportUsage)
                {
                    SB.AppendLine("#include <map>");
                }
            }

            G25.CG.Shared.Util.WriteOpenNamespace(SB, S);

            { // basic info

                {    // #define GROUP_ and GRADE_
                    SB.AppendLine("");

                    // group
                    int[] gradeBitmap = new int[S.m_dimension + 1];
                    for (int i = 0; i < S.m_GMV.NbGroups; i++)
                    {
                        gradeBitmap[S.m_GMV.Group(i)[0].Grade()] |= 1 << i;

                        SB.Append("// group: ");
                        for (int j = 0; j < S.m_GMV.Group(i).Length; j++)
                        {
                            if (j > 0) SB.Append(", ");
                            SB.Append(S.m_GMV.Group(i)[j].ToString(S.m_basisVectorNames));
                        }
                        SB.AppendLine("");
                        SB.AppendLine("#define GROUP_" + i + " " + (1 << i));
                    }

                    // grade
                    for (int i = 0; i <= S.m_dimension; i++)
                        SB.AppendLine("#define GRADE_" + i + " " + gradeBitmap[i]);
                }

                cgd.m_cog.EmitTemplate(SB, "basicInfo", "S=", S);

                if (S.m_gmvCodeGeneration == GMV_CODE.RUNTIME)
                {
                    G25.CG.Shared.Util.WriteOpenNamespace(SB, S, G25.CG.Shared.Main.RUNTIME_NAMESPACE);
                    cgd.m_cog.EmitTemplate(SB, "runtimeGpTablesHeader", "S=", S);
                    G25.CG.Shared.Util.WriteCloseNamespace(SB, S, G25.CG.Shared.Main.RUNTIME_NAMESPACE);
                }
            }

            // #define for all specialized MV types
            if (S.m_reportUsage)
                G25.CG.CPP.SMV.WriteSMVtypeConstants(SB, S, cgd);

            { // write all class types
                // write types for all GMV (all float types)
                G25.CG.CPP.GMV.WriteGMVtypes(SB, S, cgd);

                // write types for all SMVs (all float types)
                G25.CG.CPP.SMV.WriteSMVtypes(SB, S, cgd);

                // write types for all GOM (all float types)
                if (S.m_GOM != null)
                    G25.CG.CPP.GOM.WriteGOMtypes(SB, S, cgd);

                // write types for all SOMs (all float types)
                G25.CG.CPP.SOM.WriteSOMtypes(SB, S, cgd);
            }

            { // write toString
                bool def = false;
                G25.CG.CPP.ToString.WriteToString(SB, S, cgd, def);
            }

            // write classes for all GMV (all float types)
            G25.CG.CPP.GMV.WriteGMVclasses(SB, S, cgd);

            // write classes for all SMVs (all float types)
            G25.CG.CPP.SMV.WriteSMVclasses(SB, S, cgd);

            // write classes for all GOM (all float types)
            if (S.m_GOM != null)
                G25.CG.CPP.GOM.WriteGOMclasses(SB, S, cgd);

            // write classes for all SOMs (all float types)
            G25.CG.CPP.SOM.WriteSOMclasses(SB, S, cgd);

            // write constant declarations
            G25.CG.CPP.Constants.WriteDeclarations(SB, S, cgd);

            // write report usage
            cgd.m_cog.EmitTemplate(SB, (S.m_reportUsage) ? "ReportUsageHeader" : "NoReportUsageHeader");

            // set to zero / copy floats
            bool hasDouble = false;
            foreach (FloatType FT in S.m_floatTypes)
            {
                if (FT.type == "double") hasDouble = true;
                cgd.m_cog.EmitTemplate(SB, "float_zero_copy_decl", "S=", S, "FT=", FT, "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);
            }
            if (S.m_parserType == PARSER.ANTLR && (!hasDouble))
                cgd.m_cog.EmitTemplate(SB, "float_zero_copy_decl", "S=", S, "FT=", new G25.FloatType("double", "", ""), "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);

            SB.AppendLine("// decl SB:");
            SB.Append(cgd.m_declSB);

            // write operators
            if (S.m_inlineOperators)
                Operators.WriteOperatorDefinitions(SB, S, cgd);
            else Operators.WriteOperatorDeclarations(SB, S, cgd);

            // set to zero / copy floats
            foreach (FloatType FT in S.m_floatTypes)
            {
                cgd.m_cog.EmitTemplate(SB, "float_zero_copy_def", "S=", S, "FT=", FT, "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);
            }
            if (S.m_parserType == PARSER.ANTLR && (!hasDouble))
                cgd.m_cog.EmitTemplate(SB, "float_zero_copy_def", "S=", S, "FT=", new G25.FloatType("double", "", ""), "MAX_N=", G25.CG.Shared.Main.MAX_EXPLICIT_ZERO);

            SB.AppendLine("// inline def SB:");
            SB.Append(cgd.m_inlineDefSB);

            // parser declarations:
            if (S.m_parserType == PARSER.ANTLR)
                cgd.m_cog.EmitTemplate(SB, "ANTLRparserHeader", "S=", S, "FT=", S.m_floatTypes[0]);
            else if (S.m_parserType == PARSER.BUILTIN)
                cgd.m_cog.EmitTemplate(SB, "CustomParserHeader", "S=", S, "FT=", S.m_floatTypes[0]);

            // close namespace
            G25.CG.Shared.Util.WriteCloseNamespace(SB, S);

            // All this thank to Jim Lazy^H^H^H^HIdle who's too lazy to write a true C++ target for ANTLR. Thanks Jim.
            if (S.m_parserType == PARSER.ANTLR)
            {
                FloatType FT = Parser.GetANTLRfloatType(S);
                cgd.m_cog.EmitTemplate(SB, "ANTLRparserHeader_outsideNamespace", "S=", S, "FT=", FT);
            }

            // close include guard
            G25.CG.Shared.Util.WriteCloseIncludeGuard(SB, GetRawHeaderFilename(S));

            // write all to file
            G25.CG.Shared.Util.WriteFile(headerFilename, SB.ToString());

            return generatedFiles;
        }
Example #4
0
        public static List<string> GenerateCode(Specification S, G25.CG.Shared.CGdata cgd)
        {
            // get filename, list of generated filenames
            List<string> generatedFiles = new List<string>();
            string sourceFilename = S.GetOutputPath(GetRawSourceFilename(S));
            generatedFiles.Add(sourceFilename);

            // get StringBuilder where all generated code goes
            StringBuilder SB = new StringBuilder();

            // output license, copyright
            G25.CG.Shared.Util.WriteCopyright(SB, S);
            G25.CG.Shared.Util.WriteLicense(SB, S);

            { // #includes
                SB.AppendLine("#include <stdio.h>");
                SB.AppendLine("#include <utility> // for std::swap");
                if (cgd.GetFeedback(G25.CG.Shared.Main.NEED_TIME) == "true")
                    SB.AppendLine("#include <time.h> /* used to seed random generator */");
                SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.CPP.Header.GetRawHeaderFilename(S)) + "\"");
            }

            G25.CG.Shared.Util.WriteOpenNamespace(SB, S);

            GenerateTables(S, cgd, SB);

            // the list of names of smv types
            G25.CG.CPP.SMV.WriteSMVtypenames(SB, S, cgd);

            // write constant definitions
            G25.CG.CPP.Constants.WriteDefinitions(SB, S, cgd);

            // write report usage
            cgd.m_cog.EmitTemplate(SB, (S.m_reportUsage) ? "ReportUsageSource" : "NoReportUsageSource");

            if (S.m_gmvCodeGeneration == GMV_CODE.RUNTIME)
            {
                G25.CG.Shared.Util.WriteOpenNamespace(SB, S, G25.CG.Shared.Main.RUNTIME_NAMESPACE);

                cgd.m_cog.EmitTemplate(SB, "runtimeGpTablesDefs", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "bitmapGp", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "runtimeGpTable", "S=", S);
                foreach (G25.FloatType FT in S.m_floatTypes)
                {
                    cgd.m_cog.EmitTemplate(SB, "runtimeComputeGp", "S=", S, "FT=", FT);
                }
                cgd.m_cog.EmitTemplate(SB, "runtimeGpInitTables", "S=", S);
                cgd.m_cog.EmitTemplate(SB, "runtimeGpFreeTables", "S=", S);
                G25.CG.Shared.Util.WriteCloseNamespace(SB, S, G25.CG.Shared.Main.RUNTIME_NAMESPACE);
            }

            { // write toString
                bool def = true;
                G25.CG.CPP.ToString.WriteToString(SB, S, cgd, def);
            }

            // write operators
            if (!S.m_inlineOperators)
                Operators.WriteOperatorDefinitions(SB, S, cgd);

            SB.AppendLine("// def SB:");
            SB.Append(cgd.m_defSB);

            // close namespace
            G25.CG.Shared.Util.WriteCloseNamespace(SB, S);

            // write all to file
            G25.CG.Shared.Util.WriteFile(sourceFilename, SB.ToString());

            return generatedFiles;
        }
Example #5
0
        /// <summary>
        /// Generates a complete testing suite, including a main function.
        /// 
        /// The testing suite should test every function implemented, but depends
        /// on the code generator plugins to actually generate those tests.
        /// </summary>
        /// <param name="S">Specification of algebra.</param>
        /// <param name="cgd">Used to pass all kinds of info around.</param>
        /// <param name="FGI">Info about what FunctionGenerator to use for each FGS. Recycled from
        /// Function.WriteFunctions() for efficiency.</param>
        /// <returns></returns>
        public static List<string> GenerateCode(Specification S, G25.CG.Shared.CGdata cgd, G25.CG.Shared.FunctionGeneratorInfo FGI)
        {
            System.Console.WriteLine("Generating test suite . . .\n");
            cgd.SetDependencyPrefix(""); // we want missing dependencies to actually compile, and not generate an error based on function name

            // get list of generated filenames
            List<string> generatedFiles = new List<string>();
            string sourceFilename = S.GetOutputPath(G25.CG.C.TestSuite.GetRawTestSuiteFilename(S));
            generatedFiles.Add(sourceFilename);

            // reset code in cgd (get rid of all the code that was generated for the regular non-testsuite output)
            cgd.ResetSB();

            // get StringBuilder where all generated code goes
            StringBuilder SB = new StringBuilder();

            // #include all relevant headers
            SB.AppendLine("#include <time.h> /* used to seed random generator */");
            SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.Header.GetRawHeaderFilename(S)) + "\"");
            if (S.m_parserType != PARSER.NONE)
            {
                //                        SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.Parser.GetRawParserSourceFilename(S)) + "\"");
                if (S.m_parserType == PARSER.ANTLR)
                {
                    SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.Parser.GetANTLRlexerHeaderFilename(S)) + "\"");
                    SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.Parser.GetANTLRparserHeaderFilename(S)) + "\"");
                }
            }
            if (cgd.GetFeedback(G25.CG.C.MainGenerator.MERSENNE_TWISTER) == "true")
                SB.AppendLine("#include \"" + S.GetOutputFilename(G25.CG.C.RandomMT.GetRawMtHeaderFilename(S)) + "\"");

            // generate declarations for parts of the geometric product, dual, etc (works in parallel internally)
            try
            {
                bool declOnly = true;
                G25.CG.Shared.PartsCode.GeneratePartsCode(S, cgd, declOnly);
            }
            catch (G25.UserException E) { cgd.AddError(E); }

            // generate extra declarations
            foreach (FloatType FT in S.m_floatTypes)
            {
                // output internal compress function for all float types
                cgd.m_cog.EmitTemplate(cgd.m_defSB, "compress_decl",
                    "S=", S,
                    "FT=", FT,
                    "gmv=", S.m_GMV);
            }

            cgd.m_cog.EmitTemplate(cgd.m_defSB, "swapPointerDecl");

            // reset generated code (StringBuilders) of all CGDs
            for (int i = 0; i < FGI.m_functionFGS.Count; i++)
                if (FGI.m_functionGenerators[i] != null)
                    FGI.m_functionGenerators[i].ResetCGdata();

            // figure out all dependencies
            // Note that m_errors and m_missingDependencies of each function cgd are shared with main cgd, but a mutex is used to avoid racing
            {
                // serial:
                for (int i = 0; i < FGI.m_functionFGS.Count; i++)
                {
                    if (FGI.m_functionGenerators[i] != null)
                    {
                        FGI.m_functionGenerators[i].CheckTestingDepenciesEntryPoint();
                    }
                }/*
                // in parallel:
                Thread[] depFunctionThreads = new Thread[FGI.m_functionFGS.Count];
                for (int i = 0; i < FGI.m_functionFGS.Count; i++)
                {
                    if (FGI.m_functionGenerators[i] != null)
                    {
                        depFunctionThreads[i] = new Thread(FGI.m_functionGenerators[i].CheckTestingDepenciesEntryPoint);
                    }
                }
                G25.CG.Shared.Threads.StartThreadArray(depFunctionThreads);
                G25.CG.Shared.Threads.JoinThreadArray(depFunctionThreads);*/
            }

            // get random number generator FGS for each float type
            List<string> randomNumberGenerators = new List<string>();
            List<string> randomNumberTimeSeedFuncs = new List<string>();
            foreach (FloatType FT in S.m_floatTypes)
            {
                //bool returnTrueName = true;
                string funcName = G25.CG.Shared.Dependencies.GetDependency(S, cgd, "random_" + FT.type, new String[0], FT, null);
                randomNumberGenerators.Add(funcName);
                randomNumberTimeSeedFuncs.Add(funcName + "_timeSeed");
            }

            // for parsing test, subtract is required (first float type):
            string subtractGmvFuncName = G25.CG.Shared.Dependencies.GetDependency(S, cgd, "subtract", new String[] { S.m_GMV.Name, S.m_GMV.Name }, S.m_GMV.Name, S.m_floatTypes[0], null);
            string randomVersorFuncName = G25.CG.Shared.Dependencies.GetDependency(S, cgd, "random_versor", new String[0], S.m_GMV.Name, S.m_floatTypes[0], null);

            // for metric / getter setter:
            Dictionary<string, string> randomVersorFuncNames = new Dictionary<string, string>();
            Dictionary<string, string> gpGmvFuncName = new Dictionary<string, string>();
            // geometric product of all metrics and float types:
            {
                foreach (FloatType FT in S.m_floatTypes)
                {
                    randomVersorFuncNames[FT.type] = G25.CG.Shared.Dependencies.GetDependency(S, cgd, "random_versor", new String[0], S.m_GMV.Name, FT, null);
                    foreach (Metric M in S.m_metric)
                        gpGmvFuncName[FT.type + "_" + M.m_name] = G25.CG.Shared.Dependencies.GetDependency(S, cgd, "gp", new String[] { S.m_GMV.Name, S.m_GMV.Name }, S.m_GMV.Name, FT, M.m_name);
                }
            }

            { // iteratively get all dependencies and generate their code
                int count = 0;
                List<fgs> missingFunctions = null, alreadyGeneratedMissingFunctions = new List<fgs>();
                do
                {
                    count = cgd.m_missingDependencies.Count; // we loop until the number of dependencies doesn't grow anymore
                    missingFunctions = cgd.GetMissingDependenciesList(alreadyGeneratedMissingFunctions);
                    G25.CG.Shared.Functions.WriteFunctions(S, cgd, null, Functions.GetFunctionGeneratorPlugins(cgd), missingFunctions);
                    alreadyGeneratedMissingFunctions.AddRange(missingFunctions);
                } while (count < cgd.m_missingDependencies.Count);
            }

            // write code for all dependencies to output
            SB.AppendLine("// Missing dependencies declarations:");
            SB.Append(cgd.m_declSB);
            SB.AppendLine("// Missing dependencies inline definitions:");
            SB.Append(cgd.m_inlineDefSB);
            SB.AppendLine("// Missing dependencies definitions:");
            SB.Append(cgd.m_defSB);

            cgd.ResetSB();

            List<string> testFunctionNames = new List<string>(); // list of names of bool functionName(void) goes here

            { // write all 'other' test code
                // metric
                testFunctionNames.AddRange(WriteMetricTests(S, cgd, gpGmvFuncName));

                // converters
                // todo . . .

                // get / set coordinates
                testFunctionNames.AddRange(WriteGetterSetterTests(S, cgd, randomNumberGenerators, randomVersorFuncNames));

                // toString & parser (if available)
                if (S.m_parserType != PARSER.NONE)
                    testFunctionNames.Add(WriteParserTest(S, cgd, randomNumberGenerators[0], randomVersorFuncName, subtractGmvFuncName));

            }

            { // write all test functions
                // old, serial code:
                /*for (int i = 0; i < FGI.m_functionFGS.Count; i++)
                {
                    if (FGI.m_functionGenerators[i] != null)
                    {
                        FGI.m_functionGenerators[i].WriteTestFunctionEntryPoint();
                    }
                }*/
                { // in parallel:
                    Thread[] testFunctionThreads = new Thread[FGI.m_functionFGS.Count];
                    for (int i = 0; i < FGI.m_functionFGS.Count; i++)
                    {
                        if (FGI.m_functionGenerators[i] != null)
                        {
                            testFunctionThreads[i] = new Thread(FGI.m_functionGenerators[i].WriteTestFunctionEntryPoint);
                        }
                    }
                    G25.CG.Shared.Threads.StartThreadArray(testFunctionThreads);
                    G25.CG.Shared.Threads.JoinThreadArray(testFunctionThreads);
                }

                // collect all the results from the threads:
                for (int f = 0; f < FGI.m_functionFGS.Count; f++)
                {
                    if (FGI.m_functionGenerators[f] != null)
                    {
                        if (FGI.m_functionCgd[f].m_generatedTestFunctions != null)
                            testFunctionNames.AddRange(FGI.m_functionCgd[f].m_generatedTestFunctions);

                        cgd.m_declSB.Append(FGI.m_functionCgd[f].m_declSB);
                        cgd.m_defSB.Append(FGI.m_functionCgd[f].m_defSB);
                        cgd.m_inlineDefSB.Append(FGI.m_functionCgd[f].m_inlineDefSB);
                    }
                }
            }

            // write code for all testing code to output
            SB.AppendLine("// Testing code declarations:");
            SB.Append(cgd.m_declSB);
            SB.AppendLine("// Testing code inline definitions:");
            SB.Append(cgd.m_inlineDefSB);
            SB.AppendLine("// Testing code definitions:");
            SB.Append(cgd.m_defSB);

            // write main function
            cgd.m_cog.EmitTemplate(SB, "testSuiteMain",
                "S=", S,
                "testFunctionNames=", testFunctionNames.ToArray(),
                "randomNumberSeedFunctionNames=", randomNumberTimeSeedFuncs.ToArray());

            // write all to file
            G25.CG.Shared.Util.WriteFile(sourceFilename, SB.ToString());

            return generatedFiles;
        }