/// <summary>
        /// Generates source-target pairs among the previously generated Chirper users.  
        /// </summary>
        /// <param name="graphDoc">The GraphML document that the code is building.</param>
        /// <param name="duplicateEdgeStatistic">
        ///     A passed in statistic that will be incremented each time a duplicate edge is generated.
        /// </param>
        /// <returns>The length of time used to generate the edges among the nodes.</returns>
        private TimeSpan GenerateChirperFollowerEdge(ChirperGraphMLDocument graphDoc, ref int duplicateEdgeStatistic)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            int edgeId = this.edgeIDStartValue;
            int maxNodeId = this.nodeIdStartValue + this.networkNodeCount;
            int minNodeId = this.edgeNodeStartIdValue;
            Random randomGenerator = new Random();

            Console.Write("\t");
            while (edgeId < this.networkEdgeCount + this.edgeIDStartValue)
            {
                int source = 0;
                int target = 0;
                if (this.random)
                {
                    // Use while loops to prevent keeping source and target values of zero.
                    while (source == 0)
                    {
                        source = randomGenerator.Next(this.nodeIdStartValue, maxNodeId);
                    }

                    while (target == 0)
                    {
                        target = randomGenerator.Next(this.nodeIdStartValue, maxNodeId);
                    }
                }
                else
                {
                    int relativeEdgeId = edgeId - this.edgeIDStartValue;
                    int edgesPerNode = this.networkEdgeCount / this.networkNodeCount;
                    int relativeSource = relativeEdgeId / edgesPerNode;
                    int relativeTarget = (relativeSource + 1 + (relativeEdgeId % edgesPerNode));
                    int range = maxNodeId - minNodeId;
                    source = relativeSource + minNodeId;
                    target = (relativeTarget % range) + minNodeId;
                }

                // Don't allow the source to be equal to the target.
                while (source == target)
                {
                    target = randomGenerator.Next(minNodeId, maxNodeId);
                }
                if (graphDoc.AddEdge(edgeId, source, target))
                {
                    // Don't increment if the add fails due to a duplicate.
                    edgeId++;
                }
                else
                {
                    duplicateEdgeStatistic++;
                }

                if ((edgeId % ProgressInterval) == 0) Console.Write("."); // Show progress
            }
            Console.WriteLine(".");
            stopwatch.Stop();
            return TimeSpan.FromSeconds(stopwatch.Elapsed.TotalSeconds);
        }
 /// <summary>
 /// Generates Chirper "User Ids" and adds them to the GraphML document.
 /// </summary>
 /// <param name="graphDoc">The GraphML document that the code is building.</param>
 /// <returns>The length of time used to generate the nodes.</returns>
 private TimeSpan GenerateChirperUserNodes(ChirperGraphMLDocument graphDoc)
 {
     Stopwatch stopwatch = new Stopwatch();
     stopwatch.Start();
     Console.Write("\t");
     for (int nodeIndex = this.nodeIdStartValue; nodeIndex < this.networkNodeCount + nodeIdStartValue; nodeIndex++)
     {
         // Since we are using a for loop, these will never be duplicates.
         // If the method of generating the indexes is changed to one that could produce duplicates,
         // the return value of AddNode() must be taken into account.
         string userId = "U" + nodeIndex.ToString(CultureInfo.InvariantCulture);
         graphDoc.AddNode(nodeIndex, userId);
         if ((nodeIndex % ProgressInterval) == 0) Console.Write("."); // Show progress
     }
     Console.WriteLine(".");
     stopwatch.Stop();
     TimeSpan nodeGenerationTime = TimeSpan.FromSeconds(stopwatch.Elapsed.TotalSeconds);
     return nodeGenerationTime;
 }
        internal int Run()
        {
            string thisProcessName = Process.GetCurrentProcess().ProcessName;
            using (PerformanceCounter memoryMonitor = new PerformanceCounter("Process", "Working Set", thisProcessName))
            {
                memoryMonitor.NextSample();
                using (PerformanceCounter processorMonitor = new PerformanceCounter("Process", "% Processor Time", thisProcessName))
                {
                    processorMonitor.NextSample();
                    networkGeneratorLog = new List<string>();
                    LogMessage("********************************************************");
                    LogMessage(string.Format("{0,25}\t{1:G}", "Start Network Generation:", DateTime.Now));
                    LogMessage(string.Format("{0,25}\t{1:N0}\tEdges: {2:N0}", "Requested Nodes:", this.networkNodeCount, this.networkEdgeCount));
                    LogMessage(string.Format("{0,25}\t{1:N0}\tEdges: {2:N0}", "Start Ids - Nodes:", this.nodeIdStartValue, this.edgeIDStartValue));
                    ChirperGraphMLDocument graphDoc = new ChirperGraphMLDocument();
                    LogMessage(string.Empty);
                    LogMessage("Starting node generation...");

                    TimeSpan nodeGenerationTime = GenerateChirperUserNodes(graphDoc);
                    LogMessage("\tNode generation complete in " + nodeGenerationTime);
                    LogPerformanceCounterData(memoryMonitor, processorMonitor);

                    LogMessage(string.Empty);
                    FlushLog();

                    int duplicateEdgeStatistic = 0;
                    TimeSpan edgeGenerationTime = default(TimeSpan);
                    if (this.networkEdgeCount > 0)
                    {
                        LogMessage("Starting edge generation...");
                        edgeGenerationTime = GenerateChirperFollowerEdge(graphDoc, ref duplicateEdgeStatistic);

                        LogMessage("\tEdge generation complete in " + edgeGenerationTime);
                        LogMessage("\tDuplicates generated: " + duplicateEdgeStatistic);
                        LogPerformanceCounterData(memoryMonitor, processorMonitor);
                        LogMessage(string.Empty);
                    }
                    else
                    {
                        LogMessage("Edge generation suppressed from command line.");
                        LogMessage(string.Empty);
                    }

                    Stopwatch stopwatch = new Stopwatch();
                    FlushLog();

                    LogMessage("Starting GraphML File write...");
                    stopwatch.Restart();
                    graphDoc.WriteXmlWithWriter(this.graphMLFile);
                    stopwatch.Stop();
                    TimeSpan xmlWriteTime = TimeSpan.FromSeconds(stopwatch.Elapsed.TotalSeconds);
                    LogMessage("\tGraphML file write complete in " + xmlWriteTime);
                    LogPerformanceCounterData(memoryMonitor, processorMonitor);

                    LogMessage(string.Empty);
                    LogMessage(string.Format(CultureInfo.InvariantCulture, "{0,30}\t{1}", "Total Graph Generation Time:", (nodeGenerationTime + edgeGenerationTime + xmlWriteTime)));
                    LogMessage(string.Empty);
                    FlushLog();

                    // Get the size of the file so we can add the statistic.
                    FileInfo fileInfo = new FileInfo(this.graphMLFile);
                    fileInfo.Refresh();
                    long fileLength = fileInfo.Length;
                    string range = "bytes";
                    if (fileLength > Kilo)
                    {
                        fileLength = fileLength / Kilo;
                        range = "KB";
                    }
                    if (fileLength > Kilo)
                    {
                        fileLength = fileLength / Kilo;
                        range = "MB";
                    }

                    LogMessage(string.Format(CultureInfo.InvariantCulture, "{0,30}\t{1:N0} {2}", "GraphML File Size:", fileLength, range));

                    LogMessage(string.Empty);
                    FlushLog();

                }
            }

            // Using Linq to XML here to insert the statistic comment won't adversely affect performance.
            //XComment statisticsComment = new XComment(summaryMessageStringBuilder.ToString());
            //XDocument generatedGraphMLDoc = XDocument.Load(graphMLFile);
            //XElement graphElement = generatedGraphMLDoc.Root;
            //graphElement.AddFirst(statisticsComment);
            //graphElement.Document.Save(this.graphMLFile);

            return 0;
        }