private AsynchSimulationResult ComputeSimulationResults()
        {
            // These double arrays store the double floating point result value for the simulation iteration at
            // the index value.  These distributions are then analyzed as probability models.
            double[] completionTimes = new double[this.IterationCount];
            double[] resourceCosts   = new double[this.IterationCount];
            double[] fixedCosts      = new double[this.IterationCount];

            // The resourceTime dictionary has the employee names as the key and the time usage distribution
            // as the value.
            var resourceTime = _workingProject.Organization.Employees.ToDictionary(organizationEmployee => organizationEmployee.Name, organizationEmployee => new double[IterationCount]);

            // The taskStartTimes dictionary has the task ID as the key and the starting time distribution as the value
            var taskStartTimes = _workingProject.Tasks.ToDictionary(x => x.Id, x => new double[IterationCount]);

            // The taskEndTimes dictionary has the task ID as the key and the task ending time distribution as the value
            var taskEndTimes = _workingProject.Tasks.ToDictionary(x => x.Id, x => new double[IterationCount]);

            // The fixedCostsByCategory dictionary has the fixed cost category name as the key and the category total cost
            // distribution as the value
            var categories           = new HashSet <string>(_workingProject.FixedCosts.Select(x => x.Category));
            var fixedCostsByCategory = categories.ToDictionary(category => category, category => new double[this.IterationCount]);

            var options = new ParallelOptions();

            options.MaxDegreeOfParallelism = -1;

            Parallel.For(0, IterationCount, options, i =>
            {
                var simulator = new ProjectSimulator(_workingProject);
                var result    = simulator.Simulate();

                completionTimes[i] = result.TotalCompletionDays;
                resourceCosts[i]   = result.TotalResourceCost();

                // Aggregate the usage times for each resource, by name
                foreach (string resourceName in result.ResourceUtilization.Keys)
                {
                    resourceTime[resourceName][i] = result.ResourceUtilization[resourceName].Select(x => x.Amount).Sum();
                }

                // Aggregate the distribution of start and end times for each task
                foreach (var taskId in result.TaskStartTime.Keys)
                {
                    taskStartTimes[taskId][i] = result.TaskStartTime[taskId];
                    taskEndTimes[taskId][i]   = result.TaskEndTime[taskId];
                }

                // Aggregate the fixed costs
                double totalFixedCost = 0;
                foreach (string category in categories)
                {
                    // Get the keys of the fixed costs in this category
                    var keys = _workingProject.FixedCosts.Where(x => x.Category == category).Select(x => x.Id).ToList();

                    // Get the total cost of these keys
                    double categoryCost = keys.Select(x => result.FixedCostValues[x]).Sum();
                    totalFixedCost     += categoryCost;
                    fixedCostsByCategory[category][i] = categoryCost;
                }
                fixedCosts[i] = totalFixedCost;
            });

            var resultDistributions = new List <ProbabilityDensityData>();
            var taskSimulationData  = new Dictionary <Guid, TaskSimulationData>();

            // Lambda to make sure that the result distribution is only added if not empty
            var addResultDistribution = new Action <ProbabilityDensityData>(x =>
            {
                // if (!x.IsEmpty)
                resultDistributions.Add(x);
            });

            // Main simulation results
            addResultDistribution(new ProbabilityDensityData(completionTimes, "Project Duration")
            {
                XLabel         = "Days",
                Category       = "Main",
                ValueFormatter = x => $"{x:N0} days"
            });
            addResultDistribution(new ProbabilityDensityData(resourceCosts, "Resource Costs")
            {
                XLabel         = "Dollars",
                Category       = "Main",
                ValueFormatter = x => (x > 1000) ? $"${x / 1000:N0}k" : x.ToString("C0")
            });
            addResultDistribution(new ProbabilityDensityData(fixedCosts, "Fixed Project Costs")
            {
                XLabel         = "Dollars",
                Category       = "Main",
                ValueFormatter = x => (x > 1000) ? $"${x / 1000:N0}k" : x.ToString("C0")
            });

            // Resource Committment results
            foreach (var resourceTimeData in resourceTime)
            {
                addResultDistribution(new ProbabilityDensityData(resourceTimeData.Value, $"{resourceTimeData.Key}")
                {
                    Category       = "Resource Commitment",
                    XLabel         = "Days",
                    ValueFormatter = x => $"{x:N0} days"
                });
            }

            // Fixed cost category
            foreach (var fixedCost in fixedCostsByCategory)
            {
                addResultDistribution(new ProbabilityDensityData(fixedCost.Value, $"{fixedCost.Key}")
                {
                    XLabel         = "Dollars",
                    Category       = "Project Fixed Costs",
                    ValueFormatter = x => (x > 1000) ? $"${x / 1000:N0}k" : x.ToString("C0")
                });
            }

            // Task start and end times
            foreach (var taskStart in taskStartTimes)
            {
                var task = _workingProject.GetTaskById(taskStart.Key);
                var data = new ProbabilityDensityData(taskStart.Value, task.Name)
                {
                    Category       = "Task Start Day",
                    XLabel         = "Days",
                    ValueFormatter = x => $"Day {x:N0}"
                };
                addResultDistribution(data);

                taskSimulationData.Add(task.Id, new TaskSimulationData
                {
                    MedianStart = data.MedianValue,
                    HighStart   = data.UpperConfidence,
                    LowStart    = data.LowerConfidence
                });
            }

            foreach (var taskStart in taskEndTimes)
            {
                var task = _workingProject.GetTaskById(taskStart.Key);
                var data = new ProbabilityDensityData(taskStart.Value, task.Name)
                {
                    Category       = "Task End Day",
                    XLabel         = "Days",
                    ValueFormatter = x => $"Day {x:N0}"
                };
                addResultDistribution(data);
                taskSimulationData[task.Id].MedianEnd = data.MedianValue;
                taskSimulationData[task.Id].HighEnd   = data.UpperConfidence;
                taskSimulationData[task.Id].LowEnd    = data.LowerConfidence;
            }

            return(new AsynchSimulationResult
            {
                Distributions = resultDistributions,
                TaskData = taskSimulationData
            });
        }