Esempio n. 1
0
        /// <summary>
        /// Computes the max depth of the tree
        /// </summary>
        /// <param name="node">Root of the tree</param>
        /// <returns>Depth of the tree</returns>
        protected int ComputeDepth(GPNode node)
        {
            int nMaxChild = 0;

            if (node == null)
            {
                return(0);
            }

            //
            // Only need to probe further if we have a function node with children
            if (node is GPNodeFunction)
            {
                GPNodeFunction nodeFunc = (GPNodeFunction)node;

                //
                // Determine the child with the maximum depth
                for (int nChild = 0; nChild < nodeFunc.Children.Count; nChild++)
                {
                    int nCurrentChild = ComputeDepth(nodeFunc.Children[nChild]);
                    if (nCurrentChild > nMaxChild)
                    {
                        nMaxChild = nCurrentChild;
                    }
                }
            }

            return(nMaxChild + 1);              // +1 is the current node depth
        }
Esempio n. 2
0
        /// <summary>
        /// Counts the number of leaf nodes in the branch
        /// </summary>
        /// <param name="node">Root of the tree</param>
        /// <returns>Number of leaf nodes in the branch</returns>
        protected ushort DoCountLeafNodes(GPNode node)
        {
            if (node == null)
            {
                return(0);
            }
            //
            // If a terminal node, that's it, plus, it's also a leaf node by definition
            if (node is GPNodeTerminal)
            {
                return(1);
            }

            //
            // Otherwise, we have a functional node, so count up leafs in the children
            GPNodeFunction nodeFunc   = (GPNodeFunction)node;
            ushort         countChild = 0;

            for (int nChild = 0; nChild < nodeFunc.Children.Count; nChild++)
            {
                countChild += DoCountLeafNodes(nodeFunc.Children[nChild]);
            }
            //
            // If this is a functional node with no children, it is a terminal, so count it.
            if (nodeFunc.Children.Count == 0)
            {
                return((ushort)(countChild + 1));
            }
            return(countChild);
        }
Esempio n. 3
0
        /// <summary>
        /// Used to determine if a UDF correctly compiles.  If the function doesn't
        /// successfully compile, the errors are returned through the Errors parameter.
        /// </summary>
        /// <param name="Name">Name of the function</param>
        /// <param name="FunctionCode">function code to test</param>
        /// <param name="Errors">Array of errors, if any</param>
        /// <returns>True/False upon success/failure</returns>
        public bool ValidateUserFunction(string Name, string FunctionCode, out string[] Errors)
        {
            GPNodeFunction func = CompileUserFunction(FunctionCode, Name, out Errors);

            if (func == null)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 4
0
        /// <summary>
        /// ICloneable interface
        /// </summary>
        /// <returns>Clone of the object</returns>
        public override Object Clone()
        {
            GPNodeFunction obj = (GPNodeFunction)this.MemberwiseClone();

            obj.m_TerminalParameters = this.TerminalParameters;
            obj.m_Children           = new List <GPNode>();
            //
            // Go through the children and start copying them
            foreach (GPNode child in m_Children)
            {
                obj.m_Children.Add((GPNode)child.Clone());
            }

            return(obj);
        }
Esempio n. 5
0
        /// <summary>
        /// Add a new function to the set.  This accepts the C# code of the function,
        /// gets it compiled and added to the set.
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="Arity"></param>
        /// <param name="UserCode"></param>
        /// <returns></returns>
        public bool AddFunction(String Name, short Arity, bool TerminalParameters, String UserCode)
        {
            //
            // Make sure we don't already have the function
            if (m_FunctionSet.ContainsKey(Name.ToUpper()))
            {
                //
                // Just return true, it's not really an error, but we don't need
                // a duplicate either.
                return(true);
            }

            //
            // Write the function
            String FunctionCode = m_Compiler.WriteUserFunction(Name, Arity, UserCode);

            //
            // Get it compiled
            String[]       Errors = null;
            GPNodeFunction node   = m_Compiler.CompileUserFunction(FunctionCode, Name, out Errors);

            node.TerminalParameters = TerminalParameters;
            if (node == null)
            {
                return(false);
            }

            //
            // Add it to our set!
            m_FunctionSet.Add(Name.ToUpper(), node);
            //
            // We also keep a collection of the keys in a List<> collection
            // so we can have an 'int' indexer for the function set
            m_FunctionSetKeys.Add(Name.ToUpper());

            //
            // Check to see if the code uses "InputHistory", if it does, indicate
            // this function set requires the input history of data to be built.
            if (UserCode.Contains("InputHistory"))
            {
                m_UseInputHistory = true;
            }

            return(true);
        }
Esempio n. 6
0
        /// <summary>
        /// Constructs a Function node based upon the XML specification
        /// </summary>
        /// <param name="FunctionNode"></param>
        /// <returns></returns>
        private GPNode CreateFunction(XmlNode FunctionNode)
        {
            XmlNode     xmlType    = FunctionNode.SelectSingleNode("Function");
            XmlNodeList listParams = FunctionNode.SelectNodes("GPNode");

            GPNodeFunction gpFunction = null;

            //
            // See if we have an ADF
            if (xmlType.InnerText == "ADF")
            {
                XmlNode xmlWhichADF = FunctionNode.SelectSingleNode("WhichFunction");
                int     WhichADF    = Convert.ToInt32(xmlWhichADF.InnerText);
                gpFunction = new GPNodeFunctionADF(WhichADF, (byte)listParams.Count);
            }
            else if (xmlType.InnerText == "ADL")                // Check for an ADL
            {
                XmlNode xmlWhichADL = FunctionNode.SelectSingleNode("WhichFunction");
                int     WhichADL    = Convert.ToInt32(xmlWhichADL.InnerText);
                gpFunction = new GPNodeFunctionADL(WhichADL, (byte)listParams.Count);
            }
            else if (xmlType.InnerText == "ADR")                // Check for an ADR
            {
                XmlNode xmlWhichADR = FunctionNode.SelectSingleNode("WhichFunction");
                int     WhichADR    = Convert.ToInt32(xmlWhichADR.InnerText);
                gpFunction = new GPNodeFunctionADR(WhichADR, (byte)listParams.Count);
            }
            else
            {
                //
                // Get the correct function node type created
                GPNodeFunction func = (GPNodeFunction)m_FunctionSet[xmlType.InnerText.ToUpper()];
                gpFunction = (GPNodeFunction)func.Clone();
            }

            //
            // Build the list of parameters to this node
            foreach (XmlNode ParamNode in listParams)
            {
                gpFunction.Children.Add(ReadGPNode(ParamNode));
            }

            return(gpFunction);
        }
        /// <summary>
        /// Unfortunately, this is essentially a linear search, I should really label each
        /// node so we have a BST tree to speed up the search.
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="node"></param>
        /// <param name="nFindLabel"></param>
        /// <returns></returns>
        protected FindResult FindNode(GPNode parent, GPNode node, int nFindLabel)
        {
            //
            // Termination Criteria: If FindLabel and the node label match
            if (node.Label == nFindLabel)
            {
                FindResult find = new FindResult();

                find.Parent = (GPNodeFunction)parent;
                find.Node   = node;
                //
                // Figure out which child of the parent this is
                if (parent != null)
                {
                    GPNodeFunction nodeFunc = (GPNodeFunction)parent;
                    for (int nChild = 0; nChild < nodeFunc.Children.Count; nChild++)
                    {
                        if (node == nodeFunc.Children[nChild])
                        {
                            find.ChildNumber = nChild;
                        }
                    }
                }
                return(find);
            }

            //
            // Search the children
            if (node is GPNodeFunction)
            {
                GPNodeFunction nodeFunc = (GPNodeFunction)node;
                for (int nChild = 0; nChild < nodeFunc.Children.Count; nChild++)
                {
                    FindResult find = FindNode(nodeFunc, nodeFunc.Children[nChild], nFindLabel);
                    if (find != null)
                    {
                        return(find);
                    }
                }
            }

            return(null);
        }
Esempio n. 8
0
        /// <summary>
        /// Internal recursive method that does the actual work of counting the
        /// nodes in the tree.
        /// </summary>
        /// <param name="node"></param>
        private void DoCountNodesInternal(GPNode node)
        {
            if (node == null)
            {
                return;
            }
            //
            // If we made it this far, count the node
            node.Label = m_CountLabel++;

            //
            // Update the ADF and ADL counts
            if (node is GPNodeFunctionADF)
            {
                m_CountADFNodes++;
            }
            if (node is GPNodeFunctionADL)
            {
                m_CountADLNodes++;
            }
            if (node is GPNodeFunctionADR)
            {
                m_CountADRNodes++;
            }

            //
            // If a terminal node, that's it, just count the node
            if (node is GPNodeTerminal)
            {
                return;
            }

            //
            // Otherwise, we have a functional node, so count up the children
            GPNodeFunction nodeFunc = (GPNodeFunction)node;

            for (int nChild = 0; nChild < nodeFunc.Children.Count; nChild++)
            {
                DoCountNodesInternal(nodeFunc.Children[nChild]);
            }
        }
        /// <summary>
        /// Custom build function that creates the result producing branch
        /// of the genetic program.
        /// </summary>
        /// <param name="TreeBuild"></param>
        /// <param name="MaxDepth"></param>
        /// <param name="CurrentDepth"></param>
        /// <param name="TerminalParameters"></param>
        /// <returns></returns>
        private GPNode BuildInternal(GPEnums.TreeBuild TreeBuild, int MaxDepth, int CurrentDepth, bool TerminalParameters)
        {
            //
            // Randomly create this node
            GPNode newNode = CreateNode(TreeBuild, MaxDepth, CurrentDepth, TerminalParameters);

            //
            // If we just created a function, build its children
            if (newNode is GPNodeFunction)
            {
                GPNodeFunction node = (GPNodeFunction)newNode;
                //
                // Need to generate the appropriate number of nodes for the operation
                for (int Child = 0; Child < node.NumberArgs; Child++)
                {
                    node.Children.Add(BuildInternal(TreeBuild, MaxDepth - 1, CurrentDepth + 1, node.TerminalParameters));
                }
            }

            return(newNode);
        }
Esempio n. 10
0
        ///
        /// <summary>
        /// Take the CSharp code, compile it to an assembly and then return an instance
        /// of the class (Classname).  This method absolutely assumes it is compiling
        /// a GPNodeFunction derived object.
        /// </summary>
        /// <param name="CSharpCode">The C# code to compile</param>
        /// <param name="FunctionName">Name of the user defined function</param>
        /// <param name="Errors">List of compilation errors</param>
        /// <returns>in-memory instance of the code ready for execution</returns>
        public GPNodeFunction CompileUserFunction(String CSharpCode, String FunctionName, out String[] Errors)
        {
            CSharpCodeProvider csProvider = new CSharpCodeProvider();
            CompilerParameters csParams   = new CompilerParameters();

            csParams.GenerateExecutable    = false;
            csParams.GenerateInMemory      = true;
            csParams.TreatWarningsAsErrors = false;

            //
            // We derive from GPNodeFunction, have to reference the GPServer.exe assembly
            csParams.ReferencedAssemblies.Add(Application.StartupPath + "\\GPServer.exe");

            //
            // This does the actual compilation of the source code
            CompilerResults results = csProvider.CompileAssemblyFromSource(csParams, CSharpCode);

            //
            // See if there are any errors
            Errors = null;
            if (results.Errors.Count > 0)
            {
                //
                // Convert the errors into a string array that is send back
                Errors = new String[results.Errors.Count];
                for (int Item = 0; Item < results.Errors.Count; Item++)
                {
                    Errors[Item] = (Item + 1).ToString() + ": " + results.Errors[Item].ErrorText;
                }
                return(null);
            }

            //
            // Given the assembly, create an instance of the object from it
            GPNodeFunction codeInstance = (GPNodeFunction)results.CompiledAssembly.CreateInstance("GPStudio.Server.GPNodeFunction" + FunctionName);

            return(codeInstance);
        }
Esempio n. 11
0
        /// <summary>
        /// Restores a node from the array representation back into a tree representation
        /// </summary>
        /// <param name="FunctionSet"></param>
        /// <returns>Reference to the converted tree</returns>
        public GPNode ConvertNode(IGPFunctionSet FunctionSet)
        {
            //
            // Determine the type of the node
            // < 200 : User Defined function
            // 200 to 210 : Terminal
            // 253 : ADR
            // 254 : ADL
            // 255 : ADF
            if (m_TreeArrayNew[m_ArrayPos] == 255)
            {
                m_ArrayPos++;
                byte WhichADF             = m_TreeArrayNew[m_ArrayPos++];
                byte NumberArgs           = m_TreeArrayNew[m_ArrayPos++];
                GPNodeFunctionADF NodeNew = new GPNodeFunctionADF(WhichADF, NumberArgs);
                //
                // Build up the children before returning
                for (byte Child = 0; Child < NumberArgs; Child++)
                {
                    NodeNew.Children.Add(ConvertNode(FunctionSet));
                }
                return(NodeNew);
            }
            else if (m_TreeArrayNew[m_ArrayPos] == 254)
            {
                m_ArrayPos++;
                byte WhichADL             = m_TreeArrayNew[m_ArrayPos++];
                byte NumberArgs           = m_TreeArrayNew[m_ArrayPos++];
                GPNodeFunctionADL NodeNew = new GPNodeFunctionADL(WhichADL, NumberArgs);
                //
                // Build up the children before returning
                for (byte Child = 0; Child < NumberArgs; Child++)
                {
                    NodeNew.Children.Add(ConvertNode(FunctionSet));
                }
                return(NodeNew);
            }
            else if (m_TreeArrayNew[m_ArrayPos] == 253)
            {
                m_ArrayPos++;
                byte WhichADR             = m_TreeArrayNew[m_ArrayPos++];
                byte NumberArgs           = m_TreeArrayNew[m_ArrayPos++];
                GPNodeFunctionADR NodeNew = new GPNodeFunctionADR(WhichADR, NumberArgs);
                //
                // Build up the children before returning
                for (byte Child = 0; Child < NumberArgs; Child++)
                {
                    NodeNew.Children.Add(ConvertNode(FunctionSet));
                }
                return(NodeNew);
            }
            else if (m_TreeArrayNew[m_ArrayPos] < 200)
            {
                GPNodeFunction NodeNew = (GPNodeFunction)((GPNodeFunction)FunctionSet[m_TreeArrayNew[m_ArrayPos++]]).Clone();
                //
                // Build up this node's children first
                byte ChildCount = m_TreeArrayNew[m_ArrayPos++];
                for (byte Child = 0; Child < ChildCount; Child++)
                {
                    NodeNew.Children.Add(ConvertNode(FunctionSet));
                }
                return(NodeNew);
            }
            else             // Terminal
            {
                switch (m_TreeArrayNew[m_ArrayPos++])
                {
                case 200:
                    byte WhichADFParam             = m_TreeArrayNew[m_ArrayPos++];
                    GPNodeTerminalADFParam NodeADF = new GPNodeTerminalADFParam(WhichADFParam);
                    return(NodeADF);

                case 201:
                    byte WhichUserDefined = m_TreeArrayNew[m_ArrayPos++];
                    GPNodeTerminalUserDefined NodeUser = new GPNodeTerminalUserDefined(WhichUserDefined);
                    return(NodeUser);

                case 202:
                    double dValue;
                    unsafe
                    {
                        byte *ptr = (byte *)&dValue;
                        for (int pos = 0; pos < sizeof(double); pos++)
                        {
                            ptr[pos] = m_TreeArrayNew[m_ArrayPos++];
                        }
                    }
                    GPNodeTerminalDouble NodeDouble = new GPNodeTerminalDouble(dValue);
                    return(NodeDouble);

                case 203:
                    int nValue;
                    unsafe
                    {
                        byte *ptr = (byte *)&nValue;
                        for (int pos = 0; pos < sizeof(int); pos++)
                        {
                            ptr[pos] = m_TreeArrayNew[m_ArrayPos++];
                        }
                    }
                    GPNodeTerminalInteger NodeInteger = new GPNodeTerminalInteger(nValue);
                    return(NodeInteger);
                }
            }

            //
            // TODO: Throw an exception, instead of this lazy crap! :)
            return(null);
        }
Esempio n. 12
0
        /// <summary>
        /// Converts a tree node into an array based representation.  For functions
        /// an byte lookup is used into the FunctionSet, for terminals, the type
        /// is recorded along with whatever extra data may be needed.
        /// TODO: Get rid of the unsafe code.  I know there is a bit conversion
        /// function, I just couldn't find it quickly.
        /// </summary>
        /// <param name="Node">The node to convert</param>
        /// <param name="FunctionSet"></param>
        private void StoreNode(GPNode Node, IGPFunctionSet FunctionSet)
        {
            //
            // Figure out what kind of node we have and store it accordingly.
            if (Node is GPNodeFunction)
            {
                GPNodeFunction NodeFunction = (GPNodeFunction)Node;
                if (NodeFunction is GPNodeFunctionADF)
                {
                    GPNodeFunctionADF adf = (GPNodeFunctionADF)NodeFunction;
                    //
                    // For an ADF node,record it as value 255 and then store the which
                    // ADF it refers to and the number of arguments.
                    m_TreeArrayNew.Add((byte)255);
                    m_TreeArrayNew.Add((byte)adf.WhichFunction);
                    m_TreeArrayNew.Add(adf.NumberArgs);
                }
                else if (NodeFunction is GPNodeFunctionADL)
                {
                    GPNodeFunctionADL adl = (GPNodeFunctionADL)NodeFunction;
                    //
                    // For an ADL node,record it as value 254 and then store the which
                    // ADL it refers to and the number of arguments.
                    m_TreeArrayNew.Add((byte)254);
                    m_TreeArrayNew.Add((byte)adl.WhichFunction);
                    m_TreeArrayNew.Add(adl.NumberArgs);
                }
                else if (NodeFunction is GPNodeFunctionADR)
                {
                    GPNodeFunctionADR adr = (GPNodeFunctionADR)NodeFunction;
                    //
                    // For an ADR node,record it as value 253 and then store the which
                    // ADR it refers to and the number of arguments.
                    m_TreeArrayNew.Add((byte)253);
                    m_TreeArrayNew.Add((byte)adr.WhichFunction);
                    m_TreeArrayNew.Add(adr.NumberArgs);
                }
                else
                {
                    //
                    // Store the index of the function and then the number of children
                    // for this function
                    m_TreeArrayNew.Add((byte)FunctionSet.IndexOfKey(NodeFunction.ToStringUpper()));
                    m_TreeArrayNew.Add((byte)((GPNodeFunction)Node).Children.Count);
                }

                //
                // Store each of the children
                foreach (GPNode NodeChild in NodeFunction.Children)
                {
                    StoreNode(NodeChild, FunctionSet);
                }
            }
            else             // Terminal Node
            {
                //
                // Depending upon the type of the terminal, set the appropriate value
                if (Node is GPNodeTerminalADFParam)
                {
                    m_TreeArrayNew.Add(200);
                    m_TreeArrayNew.Add((byte)((GPNodeTerminalADFParam)Node).WhichParameter);
                }
                else if (Node is GPNodeTerminalUserDefined)
                {
                    m_TreeArrayNew.Add(201);
                    m_TreeArrayNew.Add((byte)((GPNodeTerminalUserDefined)Node).WhichUserDefined);
                }
                else if (Node is GPNodeTerminalDouble)
                {
                    m_TreeArrayNew.Add(202);
                    double dValue = ((GPNodeTerminalDouble)Node).Value;
                    unsafe
                    {
                        byte *ptr = (byte *)&dValue;
                        for (int pos = 0; pos < sizeof(double); pos++)
                        {
                            m_TreeArrayNew.Add(ptr[pos]);
                        }
                    }
                }
                else if (Node is GPNodeTerminalInteger)
                {
                    m_TreeArrayNew.Add(203);
                    int dValue = ((GPNodeTerminalInteger)Node).Value;
                    unsafe
                    {
                        byte *ptr = (byte *)&dValue;
                        for (int pos = 0; pos < sizeof(int); pos++)
                        {
                            m_TreeArrayNew.Add(ptr[pos]);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// This is a simple factory function that selects from the available
        /// list of functions and creates a class of that type.
        /// </summary>
        /// <param name="UseADF">Select from ADFs</param>
        /// <param name="StartADF">Which ADF to start choosing from</param>
        /// <param name="UseADL">Select from ADLs</param>
        /// <param name="StartADL">Which ADL to start choosing from</param>
        /// <param name="UseADR">Select from ADRs</param>
        /// <param name="StartADR">Which ADR to start choosing from</param>
        protected GPNode CreateNodeFunction(bool UseADF, int StartADF, bool UseADL, int StartADL, bool UseADR, int StartADR)
        {
            //
            // Add up all the different function types that we can choose from
            //		*"Regular" function types sent over from the client
            //		*ADFs
            //		*ADLs
            //		*ADRs
            int Count = m_Config.FunctionSet.Count;

            if (UseADF)
            {
                Count += m_Branch.Parent.ADF.Count - StartADF;
            }
            if (UseADL)
            {
                Count += m_Branch.Parent.ADL.Count - StartADL;
            }
            if (UseADR)
            {
                Count += m_Branch.Parent.ADR.Count - StartADR;
            }

            //
            // Randomly select from the function node choices.
            int Function = GPUtilities.rngNextInt(Count);

            //
            // See if we chose a "regular" function
            if (Function < m_Config.FunctionSet.Count)
            {
                GPNodeFunction Type = (GPNodeFunction)m_Config.FunctionSet[Function];
                return((GPNodeFunction)Type.Clone());
            }

            //
            // See if we chose an ADF
            Function -= m_Config.FunctionSet.Count;
            if (Function < (m_Branch.Parent.ADF.Count - StartADF))
            {
                int  WhichADF             = StartADF + Function;
                byte NumberArgs           = m_Branch.Parent.ADF[WhichADF].NumberArgs;
                GPNodeFunctionADF adfFunc = new GPNodeFunctionADF(WhichADF, NumberArgs);

                return(adfFunc);
            }

            //
            // See if we chose an ADL
            Function -= m_Config.ADFSet.Count;
            if (Function < (m_Branch.Parent.ADL.Count - StartADL))
            {
                int  WhichADL             = StartADL + Function;
                byte NumberArgs           = m_Branch.Parent.ADL[WhichADL].NumberArgs;
                GPNodeFunctionADL adlFunc = new GPNodeFunctionADL(WhichADL, NumberArgs);

                return(adlFunc);
            }

            //
            // See if we chose an ADR
            Function -= m_Config.ADLSet.Count;
            if (Function < (m_Branch.Parent.ADR.Count - StartADR))
            {
                int  WhichADR             = StartADR + Function;
                byte NumberArgs           = m_Branch.Parent.ADR[WhichADR].NumberArgs;
                GPNodeFunctionADR adrFunc = new GPNodeFunctionADR(WhichADR, NumberArgs);

                return(adrFunc);
            }

            return(null);
        }
Esempio n. 14
0
        //
        // Gets the function nodes written out
        private void WriteFunctionNodeBody(GPNode node)
        {
            GPNodeFunction nodeFunc = (GPNodeFunction)node;

            String sType = node.GetType().ToString();

            if (node is GPNodeFunctionADF)
            {
                GPNodeFunctionADF nodeADF = (GPNodeFunctionADF)nodeFunc;

                m_xmlWriter.WriteStartElement("Function");
                m_xmlWriter.WriteValue("ADF");
                m_xmlWriter.WriteEndElement();

                //
                // Write out "which" ADF function this is
                m_xmlWriter.WriteStartElement("WhichFunction");
                m_xmlWriter.WriteValue(nodeADF.WhichFunction);
                m_xmlWriter.WriteEndElement();

                for (int nParam = 0; nParam < nodeADF.NumberArgs; nParam++)
                {
                    WriteProgramNode(nodeADF.Children[nParam]);
                }
            }
            else if (node is GPNodeFunctionADL)
            {
                GPNodeFunctionADL nodeADL = (GPNodeFunctionADL)nodeFunc;

                m_xmlWriter.WriteStartElement("Function");
                m_xmlWriter.WriteValue("ADL");
                m_xmlWriter.WriteEndElement();

                //
                // Write out "which" ADL function this is
                m_xmlWriter.WriteStartElement("WhichFunction");
                m_xmlWriter.WriteValue(nodeADL.WhichFunction);
                m_xmlWriter.WriteEndElement();

                for (int nParam = 0; nParam < nodeADL.NumberArgs; nParam++)
                {
                    WriteProgramNode(nodeADL.Children[nParam]);
                }
            }
            else if (node is GPNodeFunctionADR)
            {
                GPNodeFunctionADR nodeADR = (GPNodeFunctionADR)nodeFunc;

                m_xmlWriter.WriteStartElement("Function");
                m_xmlWriter.WriteValue("ADR");
                m_xmlWriter.WriteEndElement();

                //
                // Write out "which" ADR function this is
                m_xmlWriter.WriteStartElement("WhichFunction");
                m_xmlWriter.WriteValue(nodeADR.WhichFunction);
                m_xmlWriter.WriteEndElement();

                for (int nParam = 0; nParam < nodeADR.NumberArgs; nParam++)
                {
                    WriteProgramNode(nodeADR.Children[nParam]);
                }
            }
            else
            {
                m_xmlWriter.WriteStartElement("Function");
                m_xmlWriter.WriteValue(nodeFunc.ToString());
                m_xmlWriter.WriteEndElement();

                //
                // Write out its children
                for (int nParam = 0; nParam < nodeFunc.NumberArgs; nParam++)
                {
                    WriteProgramNode(nodeFunc.Children[nParam]);
                }
            }
        }