using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
public class Test
{
	class Program
    {
        public static Mutex mux = new Mutex();
        static void Main(string[] args)
        {
            int number = 10000000;
            //int number = 10000;
            //int number = 100;
            var sw = Stopwatch.StartNew();
 
            int testIterations = 100;
            //int testIterations = 100000;
            //int testIterations = 1000000;
 
            int testCount = 0;
 
            while (testCount++ < testIterations)
            {
                //parallel(number);
                //parallelNoList(number);
                //notParallel(number);
                //opSolution2(number);                
                //counting(number);
                //multiPersonalize(number);
                //multi(number, new[] { 3, 5 });
                //multiPersonalizeEnhanted(number);
                //multiEnhanted(number, new[] { 3, 5 });
                unrolled(number);
            }
 
            sw.Stop();
            Console.Write("Elapsed " + sw.ElapsedMilliseconds / testCount + "ms"); //avr
            //Console.Write("Elapsed " + sw.ElapsedMilliseconds + "ms"); //total
 
            Console.ReadKey();
        }
 
        static List<int> parallel(int limit)       //434ms, 85447ms , 348600ms
        {
            return Enumerable.Range(0, limit+1).AsParallel().Where(n => n % 3 == 0 || n % 5 == 0).OrderBy(x => x).ToList();
        }
 
        static List<int> parallelEnhanted(int limit)   
        {
            List<int> finalResult = new List<int>();
            var result = Enumerable.Range(0, limit + 1).AsParallel().Where(n => n % 3 == 0 || n % 5 == 0).OrderBy(x => x);
            result.ForAll(x => 
            {
                //mux.WaitOne();
                finalResult.Add(x);
                //mux.ReleaseMutex();
            });
            return finalResult;
        }
 
        static void parallelNoList(int limit)      //136ms, 51266ms, 273409ms
        {
            Enumerable.Range(0, limit+1).AsParallel().Where(n => n % 3 == 0 || n % 5 == 0).OrderBy(x => x).ForAll(x => { });
        }
 
        static List<int> notParallel(int number)     //154ms, 14559ms, 1985ms
        {
            return Enumerable.Range(0, number+1).Where(n => n % 3 == 0 || n % 5 == 0).ToList();
        }
 
        static List<int> opSolution2(int number)        //69ms, 5791ms, 879ms
        {
            List<int> list = new List<int>();
            for (var i = 0; i <= number; i++)
            {
                if (i % 3 == 0 || i % 5 == 0)
                {
                    list.Add(i);
                }
            }
            return list;
        }
 
        static List<int> counting(int number)        //34ms, 2398ms, 521ms
        {
            List<int> list = new List<int> { 0 };
            int count3 = 0;
            int count5 = 0;
            int step3 = 3;
            int step5 = 5;
            while (count3 < number || count5 < number)
            {
                if ((count3 + step3) <= (count5 + step5))
                {
                    count3 += step3;
 
                    if(count3 == (count5 + step5)) count5 += step5;
 
                    if (count3 <= number)
                    {
                        list.Add(count3);
                    }
                }
                else
                {
                    count5 += step5;
                    if (count5 <= number)
                    {
                        list.Add(count5);
                    }
                }
 
            }
            return list;
        }
 
        static List<int> unrolled(int max)      //32ms, 2072ms, 464ms
        {
            List<int> result = new List<int> { 0 };
 
            int i = 0;
            while (true)
            {
                i += 3;
                if (i > max) break;
                result.Add(i);
                i += 2;
                if (i > max) break;
                result.Add(i);
                i += 1;
                if (i > max) break;
                result.Add(i);
                i += 3;
                if (i > max) break;
                result.Add(i);
                i += 1;
                if (i > max) break;
                result.Add(i);
                i += 2;
                if (i > max) break;
                result.Add(i);
                i += 3;
                if (i > max) break;
                result.Add(i);
            }
 
            return result;
        }
 
        static List<int> multiPersonalizeEnhanted(int max)      //37ms, 2655ms, 501ms
        {
            int[] deltas = { 3, 2, 1, 3, 1, 2, 3 };
 
            List<int> result = new List<int> { 0 };
 
            int i = 0;
            do
            {
                foreach (int delta in deltas)
                {
                    i += delta;
                    if (i > max) break;
                    result.Add(i);
                }
            } while (i < max);
 
            return result;
        }
 
        static List<int> multiPersonalize(int max)      //43ms, 3498ms, 654ms
        {
            int[] deltas = { 3, 2, 1, 3, 1, 2, 3 };
 
            List<int> result = new List<int>();
 
            int j = 0;
            for (int i = 0; i <= max; i += deltas[j++ % deltas.Length])
            {
                result.Add(i);
            }
 
            return result;
        }
 
        static List<int> multiEnhanted(int max, int[] divs)      //37ms, 2701ms, 1149ms
        {
            int[] deltas = calcDeltas(divs);
 
            List<int> result = new List<int> { 0 };
 
            int i = 0;
            do
            {
                foreach (int delta in deltas)
                {
                    i += delta;
                    if (i > max) break;
                    result.Add(i);
                }
            } while (i < max);
 
            return result;
        }
 
        static List<int> multi(int max, int[] divs)      //47ms, 3529ms, 1270ms
        {                                              
            int[] deltas = calcDeltas(divs);
 
            List<int> result = new List<int>();
 
            int j = 0;
            for (int i = 0; i <= max; i += deltas[j++ % deltas.Length])
            {
                result.Add(i);
            }
 
            return result;
        }
 
        static int[] calcDeltas(int[] divs)
        {
            long max = 1;
            foreach 
(int div in divs
) 
            List<long> ret = new List<long>();
 
            foreach 
(int div in divs
)            {
                for (int i 
= div; i 
<= max
; i 
+= div)                 {
                    int idx = ret.BinarySearch(i);
                    if (idx < 0)
                    {
                        ret.Insert(~idx, i);
                    }
                }
            }
 
            for (int i = ret.Count - 1; i > 0; i--)
                ret[i] -= ret[i - 1];
 
            return ret.ConvertAll(x => (int)x).ToArray();
        }
 
        static long gcf(long a, long b)
        {
            while (b != 0)
            {
                long temp = b;
                b = a % b;
                a = temp;
            }
            return a;
        }
 
        static long lcm(long a, long b)
        {
            return (a / gcf(a, b)) * b;
        }
 
    }
}