//
        // On an HP Z600, It takes about 38 seconds to generate
        // the first 150 million primes (about 4 million primes per second)
        //
        public static UInt32[] GeneratePrimes(UInt32 max, out UInt32 outPrimeCount)
        {
            if (max == UInt32.MaxValue)
            {
                throw new ArgumentOutOfRangeException("max", "max cannot be UInt32.MaxValue");
            }

            UInt32[] primes = new UInt32[PrimeCount.UpperBound(max)];
            UInt32   primeCount;

            UInt32         maxSquareRoot = (UInt32)Math.Sqrt(max);
            BetterBitArray isComposite   = new BetterBitArray((max + 1) >> 1);

            primes[0]  = 2;
            primes[1]  = 3;
            primeCount = 2;

            UInt32 candidatePrime;
            UInt32 incrementAmount = 0;

            for (candidatePrime = 5; candidatePrime <= maxSquareRoot; candidatePrime += incrementAmount)
            {
                if (!isComposite.Get(candidatePrime >> 1))
                {
                    //
                    // Mark all multiples of this prime as composites
                    //
                    UInt32 candidatePrimeDoubled = 2 * candidatePrime;
                    for (UInt32 j = candidatePrime * candidatePrime; j <= max; j += candidatePrimeDoubled)
                    {
                        isComposite.Assert(j >> 1);
                    }

                    //
                    // Add to the primes
                    //
                    primes[primeCount++] = candidatePrime;
                }

                incrementAmount = (incrementAmount == 2) ? 4U : 2U;
            }

            // Add the rest of the primes
            for (; candidatePrime <= max; candidatePrime += incrementAmount)
            {
                if (!isComposite.Get(candidatePrime >> 1))
                {
                    primes[primeCount++] = candidatePrime;
                }
                incrementAmount = (incrementAmount == 2) ? 4U : 2U;
            }

            outPrimeCount = primeCount;
            return(primes);
        }
        public static UInt32[] GeneratePrimes(UInt32 max, out UInt32 outPrimeCount)
        {
            if (max < 5)
            {
                throw new ArgumentOutOfRangeException("max", "max can't be too small");
            }
            if (max == UInt32.MaxValue)
            {
                throw new ArgumentOutOfRangeException("max", "max cannot be UInt32.MaxValue");
            }

            UInt32[] primes = new UInt32[PrimeCount.UpperBound(max)];
            UInt32   primeCount;

            UInt32         maxSquareRoot = (UInt32)Math.Sqrt(max);
            BetterBitArray isPrime       = new BetterBitArray(max + 1);

            // put in candidate primes
            for (UInt32 i = 1; i <= maxSquareRoot; i++)
            {
                for (UInt32 j = 1; j <= maxSquareRoot; j++)
                {
                    UInt32 n;

                    n = 4 * i + j;
                    //if (n > max) throw new InvalidOperationException();
                    if (n <= max) // Should always be true for large enough max (maybe take this out0
                    {
                        UInt32 nMod12 = n % 12;
                        if (nMod12 == 1 || nMod12 == 5)
                        {
                            isPrime.Flip(n);
                        }
                    }

                    n = 3 * i + j;
                    //if (n > max) throw new InvalidOperationException();
                    if (n <= max) // Should always be true for large enough max (maybe take this out0
                    {
                        UInt32 nMod12 = n % 12;
                        if (nMod12 == 7)
                        {
                            isPrime.Flip(n);
                        }
                    }

                    if (i > j)
                    {
                        n = 3 * i - j;
                        //if (n > max) throw new InvalidOperationException();
                        if (n <= max) // Should always be true for large enough max (maybe take this out0
                        {
                            UInt32 nMod12 = n % 12;
                            if (nMod12 == 11)
                            {
                                isPrime.Flip(n);
                            }
                        }
                    }
                }
            }

            primes[0]  = 2;
            primes[1]  = 3;
            primeCount = 2;

            UInt32 candidatePrime;

            for (candidatePrime = 5; candidatePrime <= maxSquareRoot; candidatePrime += 2)
            {
                if (isPrime.Get(candidatePrime))
                {
                    //
                    // Mark all multiples of its square as composite
                    //
                    //
                    UInt32 candidatePrimeSquared = candidatePrime * candidatePrime;
                    //UInt32 candidatePrimeSquaredThenDoubled = iSquared * 2;
                    for (UInt32 j = candidatePrimeSquared; j <= max; j += candidatePrimeSquared)
                    {
                        isPrime.Deassert(j);
                    }

                    //
                    // Add to the primes
                    //
                    primes[primeCount++] = candidatePrime;
                }
            }

            // Add the rest of the primes
            for (; candidatePrime <= max; candidatePrime += 2)
            {
                if (isPrime.Get(candidatePrime))
                {
                    primes[primeCount++] = candidatePrime;
                }
            }

            outPrimeCount = primeCount;
            return(primes);
        }