public Result Run()
        {
            PortfoliosEnsemble trials = new PortfoliosEnsemble();

            Task <Trial[]> stocks = Task <Trial[]> .Factory.StartNew(() =>
            {
                return(stocksSimulation.Run(withProfile: stocksProfile));
            });

            Task <Trial[]> bonds = Task <Trial[]> .Factory.StartNew(() =>
            {
                return(bondsSimulation.Run(withProfile: bondsProfile));
            });

            Task <Trial[]> savings = Task <Trial[]> .Factory.StartNew(() =>
            {
                return(savingsSimulation.Run(withProfile: savingsProfile));
            });

            trials.StocksTrials  = stocks.Result;
            trials.BondsTrials   = bonds.Result;
            trials.SavingsTrials = savings.Result;


            return(ProcessTrials(trials));
        }
        private Result ProcessTrials(PortfoliosEnsemble trials)
        {
            Result result = new Result();

            int    trialLength      = stocksProfile.TrialLength;
            double withdrawalAmount = stocksProfile.WithdrawalAmount +
                                      bondsProfile.WithdrawalAmount +
                                      savingsProfile.WithdrawalAmount;

            // Aggregate trials
            var portfolios = new Portfolio[MonteCarloSimulation.NUM_TRIALS];

            for (var i = 0; i < portfolios.Length; i++)
            {
                portfolios[i] = new Portfolio
                {
                    Stocks  = trials.StocksTrials[i],
                    Bonds   = trials.BondsTrials[i],
                    Savings = trials.SavingsTrials[0], // They are all the same, 0 takes least time to access probably
                    Total   = new Trial()
                };

                portfolios[i].Total.Balances = new double[trialLength];
                for (var j = 0; j < trialLength; j++)
                {
                    portfolios[i].Total.Balances[j] = trials.StocksTrials[i].Balances[j] +
                                                      trials.BondsTrials[i].Balances[j] +
                                                      trials.SavingsTrials[i].Balances[j];
                }
            }


            // Get success rate
            Task <int> successRate = new Task <int>(() =>
            {
                int numberOfSuccesses = 0;
                for (var i = 0; i < MonteCarloSimulation.NUM_TRIALS; i++)
                {
                    if (portfolios[i].Total.Balances[trialLength - 2] >= withdrawalAmount)
                    {
                        numberOfSuccesses++;
                    }
                }
                return((int)Math.Round(100 * numberOfSuccesses / (double)MonteCarloSimulation.NUM_TRIALS));
            });


            // Get percentiles
            IEnumerable <double[]> findPercentiles()
            {
                for (int i = 0; i < portfolios.Length; i++)
                {
                    // Make sure that NUM_TRIALS - 1 in MonteCarloSimulation is an even multiple of NUM_PERCENTILES - 1
                    if (i % ((portfolios.Length - 1) / (NUM_PERCENTILES - 1)) == 0)
                    {
                        yield return(portfolios[i].Total.Balances);
                    }
                }
            }

            Task <IEnumerable <double[]> > getPercentiles = new Task <IEnumerable <double[]> >(findPercentiles);


            // Look at distribution of stock returns
            Task getStocksReturns = new Task(() =>
            {
                (result.StocksReturnRateFrequencies, result.StocksReturnRateXLabels) =
                    GetDistributionOf(trials.StocksTrials.SelectMany(trial => trial.ReturnRates));
            });


            // Look at distribution of bond returns
            Task getBondsReturns = new Task(() =>
            {
                (result.BondsReturnRateFrequencies, result.BondsReturnRateXLabels) =
                    GetDistributionOf(trials.BondsTrials.SelectMany(trial => trial.ReturnRates));
            });


            Task getPeaksAndEnds = new Task(() =>
            {
                List <double> stockPeaks = new List <double>(3);
                List <double> bondPeaks  = new List <double>(3);
                List <double> stockEnds  = new List <double>(3);
                List <double> bondEnds   = new List <double>(3);

                var n = MonteCarloSimulation.NUM_TRIALS;

                double[] allStockPeaks = new double[n];
                double[] allStockEnds  = new double[n];
                double[] allBondPeaks  = new double[n];
                double[] allBondEnds   = new double[n];

                for (int i = 0; i < n; i++)
                {
                    allStockPeaks[i] = trials.StocksTrials[i].Peak;
                    allStockEnds[i]  = trials.StocksTrials[i].Final;
                    allBondPeaks[i]  = trials.BondsTrials[i].Peak;
                    allBondEnds[i]   = trials.BondsTrials[i].Final;
                }

                allStockPeaks.ParallelMergeSort();
                allStockEnds.ParallelMergeSort();
                allBondPeaks.ParallelMergeSort();
                allBondEnds.ParallelMergeSort();

                for (var i = 1; i < 4; i++)
                {
                    var index = n / 4 * i;
                    stockPeaks.Add(allStockPeaks[index] - (i == 1 ? 0 : stockPeaks.Sum()));
                    bondPeaks.Add(allBondPeaks[index] - (i == 1 ? 0 : bondPeaks.Sum()));
                    stockEnds.Add(allStockEnds[index] - (i == 1 ? 0 : stockEnds.Sum()));
                    bondEnds.Add(allBondEnds[index] - (i == 1 ? 0 : bondEnds.Sum()));
                }

                result.StocksRetirementAmounts = stockPeaks;
                result.BondsRetirementAmounts  = bondPeaks;
                result.StocksEndAmounts        = stockEnds;
                result.BondsEndAmounts         = bondEnds;
            });

            // Sort the trials
            portfolios.ParallelMergeSort(CompareTrials);


            // Run each task
            getPercentiles.Start();
            successRate.Start();
            getStocksReturns.Start();
            getBondsReturns.Start();
            getPeaksAndEnds.Start();


            result.PortfolioPercentiles = getPercentiles.Result;
            result.SuccessRate          = successRate.Result;
            getBondsReturns.Wait();
            getStocksReturns.Wait();
            getPeaksAndEnds.Wait();

            return(result);
        }