public static FlowSimulationStatisticOutput CreateOutput(List <double> simulationTimes)
        {
            FlowSimulationStatisticOutput output = new FlowSimulationStatisticOutput();

            var dp = new DurationPercentiles(simulationTimes);

            var       min     = (int)Math.Floor(dp.Durations.First());
            var       max     = (int)Math.Ceiling(dp.DurationAtPercentile(0.9995)); // limit the histogram to 99.95% of times, to hide extremes and shorten the tail
            var       buckets = (max - min);
            Histogram hist    = new Histogram(simulationTimes, buckets, min, max);

            output.HistogramValues = new double[hist.BucketCount];
            output.HistogramLabels = new double[hist.BucketCount];

            double simulationsCount = simulationTimes.Count;

            for (int i = 0; i < hist.BucketCount; i++)
            {
                var bucket = hist[i];
                output.HistogramValues[i] = (bucket.Count / simulationsCount) * 100; // % of times the simulation ended at this time
                output.HistogramLabels[i] = bucket.LowerBound;
            }

            output.Percentile50 = dp.DurationAtPercentile(0.50);
            output.Percentile70 = dp.DurationAtPercentile(0.70);
            output.Percentile85 = dp.DurationAtPercentile(0.85);
            output.Percentile95 = dp.DurationAtPercentile(0.95);
            output.Percentile99 = dp.DurationAtPercentile(0.99);

            return(output);
        }
        private static void WriteSimulationTimeHistogram(FlowSimulationStatisticOutput output)
        {
            Console.WriteLine("Simulation Time");

            for (int i = 0; i < output.HistogramValues.Length; i++)
            {
                Console.WriteLine($"{output.HistogramLabels[i]:F1} : {output.HistogramValues[i]}");
            }

            Console.WriteLine($"Percentile 50% : {output.Percentile50:F2}");
            Console.WriteLine($"Percentile 70% : {output.Percentile70:F2}");
            Console.WriteLine($"Percentile 85% : {output.Percentile85:F2}");
            Console.WriteLine($"Percentile 95% : {output.Percentile95:F2}");
            Console.WriteLine($"Percentile 99% : {output.Percentile99:F2}");
        }
        public static FlowSimulationStatisticOutput RunSimulationStatistic(double newStoryRate, double[] storyCycleTimes, int simulationRuns, int expectedCompletedStoriesMin, int expectedCompletedStoriesMax)
        {
            List <double>   simulationTimes              = new List <double>();
            List <double>   avgWorkInProgress            = new List <double>();
            DiscreteUniform completedStoriesDistribution = new DiscreteUniform(expectedCompletedStoriesMin, expectedCompletedStoriesMax);

            for (int i = 0; i < simulationRuns; i++)
            {
                var simulation = new FlowSimulation(newStoryRate, storyCycleTimes, completedStoriesDistribution.Sample());
                simulation.Run();

                simulationTimes.Add(simulation.SimulationTime);
                avgWorkInProgress.Add(simulation.AverageWorkInProgress);
            }

            FlowSimulationStatisticOutput output = FlowSimulationStatisticOutput.CreateOutput(simulationTimes);

            return(output);
        }