public void TestSummaryConcurrency(int n)
        {
            var random    = new Random(42);
            var mutations = n % 10000 + 10000;
            var concLevel = n % 5 + 1;
            var total     = mutations * concLevel;

            var    sum       = new Summary("test_summary", "helpless", new string[0]);
            var    allVars   = new double[total];
            double sampleSum = 0;
            var    tasks     = new List <Task>();

            for (var i = 0; i < concLevel; i++)
            {
                var vals = new double[mutations];
                for (var j = 0; j < mutations; j++)
                {
                    var v = random.NormDouble();
                    vals[j] = v;
                    allVars[i * mutations + j] = v;
                    sampleSum += v;
                }

                tasks.Add(Task.Run(() =>
                {
                    foreach (var v in vals)
                    {
                        sum.Observe(v);
                    }
                }));
            }

            Task.WaitAll(tasks.ToArray());

            Array.Sort(allVars);

            var m = sum.Collect().Metrics.Single().Summary;

            Assert.Equal(mutations * concLevel, (int)m.SampleCount);

            var got  = m.SampleSum;
            var want = sampleSum;

            Assert.True(Math.Abs(got - want) / want <= 0.001);

            var objectives = Summary.DefObjectives.Select(_ => _.Quantile).ToArray();

            Array.Sort(objectives);

            for (var i = 0; i < objectives.Length; i++)
            {
                var wantQ   = Summary.DefObjectives.ElementAt(i);
                var epsilon = wantQ.Epsilon;
                var gotQ    = m.Quantiles[i].quantile;
                var gotV    = m.Quantiles[i].Value;
                var minMax  = GetBounds(allVars, wantQ.Quantile, epsilon);

                Assert.False(double.IsNaN(gotQ));
                Assert.False(double.IsNaN(gotV));
                Assert.False(double.IsNaN(minMax.Item1));
                Assert.False(double.IsNaN(minMax.Item2));

                Assert.Equal(wantQ.Quantile, gotQ);
                Assert.True(gotV >= minMax.Item1);
                Assert.True(gotV <= minMax.Item2);
            }
        }
        public void TestSummaryConcurrency([Range(0, 1000000, 10000)] int n)
        {
            var random    = new Random(42);
            var mutations = n % 10000 + 10000;
            var concLevel = n % 5 + 1;
            var total     = mutations * concLevel;

            var    sum       = new Summary("test_summary", "helpless", new string[0]);
            var    allVars   = new double[total];
            double sampleSum = 0;
            var    tasks     = new List <Task>();

            for (var i = 0; i < concLevel; i++)
            {
                var vals = new double[mutations];
                for (var j = 0; j < mutations; j++)
                {
                    var v = random.NormDouble();
                    vals[j] = v;
                    allVars[i * mutations + j] = v;
                    sampleSum += v;
                }

                tasks.Add(Task.Run(() =>
                {
                    foreach (var v in vals)
                    {
                        sum.Observe(v);
                    }
                }));
            }

            Task.WaitAll(tasks.ToArray());

            Array.Sort(allVars);

            var m = sum.Collect().metric.Single().summary;

            Assert.That(m.sample_count, Is.EqualTo(mutations * concLevel));

            var got  = m.sample_sum;
            var want = sampleSum;

            Assert.That(Math.Abs(got - want) / want, Is.LessThanOrEqualTo(0.001));

            var objectives = Summary.DefObjectives.Select(_ => _.Quantile).ToArray();

            Array.Sort(objectives);

            for (var i = 0; i < objectives.Length; i++)
            {
                var wantQ   = Summary.DefObjectives.ElementAt(i);
                var epsilon = wantQ.Epsilon;
                var gotQ    = m.quantile[i].quantile;
                var gotV    = m.quantile[i].value;
                var minMax  = GetBounds(allVars, wantQ.Quantile, epsilon);

                Assert.That(gotQ, Is.Not.NaN);
                Assert.That(gotV, Is.Not.NaN);
                Assert.That(minMax.Item1, Is.Not.NaN);
                Assert.That(minMax.Item2, Is.Not.NaN);

                Assert.That(gotQ, Is.EqualTo(wantQ.Quantile));
                Assert.That(gotV, Is.GreaterThanOrEqualTo(minMax.Item1));
                Assert.That(gotV, Is.LessThanOrEqualTo(minMax.Item2));
            }
        }