public void BenchmarkSummaryObserve(int w)
        {
            var stopwatch = new Stopwatch();
            
            const int N = 100000;
            var summary = new Summary("test_summary", "helpless", new string[0]);
            var tasks = new Task[w];

            stopwatch.Start();
            for (var i = 0; i < w; i++)
            {
                tasks[i] = Task.Factory.StartNew(() =>
                {
                    for (var j = 0; j < N; j++)
                        summary.Observe(j);
                });
            }

            Task.WaitAll(tasks);
            stopwatch.Stop();

            TestContext.WriteLine($"{w} tasks doing  {N} observations took {stopwatch.Elapsed.TotalMilliseconds} milliseconds");
        }
        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));
            }
        }