/// <summary>
        /// Method that displays the found primes
        /// </summary>
        /// <param name="highest">Largest number that primes were calculated for</param>
        private static void ShowPrimes(ulong highest)
        {
            Console.WriteLine();
            if (IOFunctions.GetBoolFromUser("Would you like to see the primes? "))
            {
                int    highestNumberLen = highest.ToString().Length;
                int    resultsPerRow    = (Console.WindowWidth - 1) / (highestNumberLen + 1);
                string format           = "{0," + highestNumberLen.ToString() + "} ";

                // Use a sorted set to combine all of the lists of primes
                SortedSet <ulong> sortedPrimes = new SortedSet <ulong>();
                foreach (int key in m_Primes.Keys)
                {
                    sortedPrimes.UnionWith(m_Primes[key]);
                    m_Primes[key].Clear();
                }

                // Print the primes into columns
                int col = 0;
                foreach (ulong prime in sortedPrimes)
                {
                    Console.Write(format, prime);
                    col++;
                    if (col >= resultsPerRow)
                    {
                        Console.WriteLine();
                        col = 0;
                    }
                }
            }
        }
 /// <summary>
 /// Shows the statistics for the calculations
 /// </summary>
 private static void ShowStats()
 {
     Console.WriteLine();
     if (IOFunctions.GetBoolFromUser("Would you like to see the statistics? "))
     {
         // Output results
         Console.WriteLine();
         int primesFound = 0;
         foreach (int key in m_Primes.Keys)
         {
             Console.WriteLine("Thread {0} found {1:N0} primes.", key, m_Primes[key].Count);
             primesFound += m_Primes[key].Count;
         }
         Console.WriteLine("Total: {0:N0} primes.", primesFound);
     }
 }
        /// <summary>
        /// Main program
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.BufferHeight = 9999;
            ulong highest             = IOFunctions.GetULongFromUser("Enter largest number to test for prime: ", 2, 100000000UL);
            int   producerThreadCount = IOFunctions.GetIntFromUser("Enter number of producer threads (1 - 64): ", 1, 64);
            int   consumerThreadCount = IOFunctions.GetIntFromUser("Enter number of consumer threads (1 - 64): ", 1, 64);

            OutputHeader();
            ulong         approxNumPrimes = MathStuff.ApproximateNumberOfPrimes(highest);
            List <Thread> producerThreads = new List <Thread>();
            List <Thread> consumerThreads = new List <Thread>();

            DateTime startTime = DateTime.Now;

            // Create the producer threads
            for (int i = 0; i < producerThreadCount; i++)
            {
                Thread producer = new Thread(ProducerThread);
                producer.Start(new Tuple <int, int, ulong>(i, producerThreadCount, highest));
                producerThreads.Add(producer);
            }

            // Create the consumer threads
            for (int i = 0; i < consumerThreadCount; i++)
            {
                Thread consumer = new Thread(ConsumerThread);
                // Create the List of ulongs that the consumer will place its results into
                // Also, use the approx. # of primes to minimize list resizing
                List <ulong> primes =
                    new List <ulong>((int)(approxNumPrimes / (uint)consumerThreadCount));
                // Add that list to m_Primes, using the thread's ID as its key
                m_Primes.Add(i, primes);
                consumer.Start((int)i);
                consumerThreads.Add(consumer);
            }

            // Create the timer that will output the number of numbers left to process
            m_Timer = new Timer(CountTimerCallback, null, 500, 500);

            // Wait for producer threads...
            foreach (Thread thread in producerThreads)
            {
                thread.Join();
            }
            // Signal to all consumers that no more values will be produced
            m_ProducersComplete.Set();

            // Wait for consumer threads...
            foreach (Thread thread in consumerThreads)
            {
                thread.Join();
            }


            m_Timer.Dispose();

            DateTime endTime = DateTime.Now;

            CountTimerCallback(null);
            Console.SetCursorPosition(0, 8);
            Console.WriteLine("Processing took: {0} ms", endTime.Subtract(startTime).TotalMilliseconds);

            ShowStats();

            ShowPrimes(highest);

            Console.WriteLine();
            Console.Write("Press <ENTER> to quit...");
            Console.ReadLine();
        }