public IList <PrizeResultRow> CombinePrizeResultTables(IList <PrizeResultRow> table1, IList <PrizeResultRow> table2)
        {
            if (table1 == null || table2 == null)
            {
                throw new ArgumentException("prize results tables must not be null");
            }
            //check that both tables have the same number of entries
            if (table1.Count != table2.Count)
            {
                throw new ArgumentException("pull results tables must be the same size");
            }
            //check that both tables have the same schema (prize cat names, prizename, prizeindex
            int rowCount = table1.Count;

            for (int counter = 0; counter < rowCount; counter++)
            {
                PrizeResultRow table1Row = table1[counter];
                PrizeResultRow table2Row = table2[counter];

                if (table1Row.PrizeIndex != table2Row.PrizeIndex ||
                    table1Row.PrizeCategoryName != table2Row.PrizeCategoryName ||
                    table1Row.PrizeName != table2Row.PrizeName)
                {
                    throw new ArgumentException("table1 and table 2 must have the same schema with respect to PrizeIndex, PrizeName, PrizeCategoryName");
                }
            }

            IList <PrizeResultRow> combinedPrizeResultsTable = table1.Zip(table2, (t1, t2) =>
                                                                          new PrizeResultRow()
            {
                PrizeIndex         = t1.PrizeIndex,
                PrizeCategoryName  = t1.PrizeCategoryName,
                PrizeName          = t1.PrizeName,
                PrizeSelectedCount = t1.PrizeSelectedCount + t2.PrizeSelectedCount
            }).ToList();

            return(combinedPrizeResultsTable);
        }
        public IList <PrizeResultRow> SelectPrizes(IList <SelectionDomain> selectionDomains, Random random = null)
        {
            #region Validations
            foreach (var selectionDomain in selectionDomains)
            {
                if (selectionDomain.PrizesToSelectFromDomainCount <= 0)
                {
                    throw new ArgumentException($"PrizesToSelectFromDomainCount for SelectFromDomain {selectionDomain.SelectionDomainName} must be greater than 0");
                }
                if (!_prizeSelectionTableHelper.IsPrizeSelectionTableValid(selectionDomain.PrizeSelectionTable))
                {
                    throw new ArgumentException($"PrizeSelectionTable for selectionDomain {selectionDomain.SelectionDomainName} was invalid");
                }
            }
            #endregion

            Stopwatch sw = Stopwatch.StartNew();

            //Set up variables and structures

            if (random == null)
            {
                random = new Random();
            }

            IList <PrizeNameCategoryPair> uniquePrizeNames = GetUniquePrizeNameCategoryPairs(selectionDomains);

            int finalPrizeRowCount = uniquePrizeNames.Count();

            IList <PrizeResultRow> prizeResultTable = new List <PrizeResultRow>(finalPrizeRowCount);

            List <IDictionary <int, int> > resultSummariesList = new List <IDictionary <int, int> >();


            //perform the actual selection and counting
            for (int domainCounter = 0; domainCounter < selectionDomains.Count; domainCounter++)
            {
                SelectionDomain currentSelectionDomain = selectionDomains[domainCounter];

                //for intermediate use because we need to update the prize counts in a performant way.
                IDictionary <int, int> resultsSummary = _prizeResultsTableHelper.GetEmptyPrizeResultsSummary(currentSelectionDomain.PrizeSelectionTable.Count);

                for (int counter = 0; counter < currentSelectionDomain.PrizesToSelectFromDomainCount; counter++)
                {
                    PrizeSelectionRow selectedPrizeRow = SelectPrizeFromPrizeTable(currentSelectionDomain.PrizeSelectionTable, random.NextDouble());

                    if (selectedPrizeRow != null)
                    {
                        resultsSummary[selectedPrizeRow.PrizeIndex]++;
                    }
                }

                resultSummariesList.Add(resultsSummary);
            }

            //now that we have the counts of selected prizes by index, we need to translate them back to the more
            //informative structure of the prize results table

            //generate table with all the data except counts and indexes
            prizeResultTable = uniquePrizeNames.Select(psr =>
                                                       new PrizeResultRow()
            {
                PrizeCategoryName  = psr.CategoryName,
                PrizeIndex         = 0,
                PrizeName          = psr.PrizeName,
                PrizeSelectedCount = 0
            }).ToList();

            //put in indexes
            for (int i = 0; i < finalPrizeRowCount; i++)
            {
                prizeResultTable[i].PrizeIndex = i + 1;
            }



            //now insert the counts; assumption is that the keys of the generated resultsSummary dictionary must match in number
            //and start at the same index (1), as the largestSelectionDomain which is used to prepopulate the prizeResultTable
            for (int domainCounter = 0; domainCounter < selectionDomains.Count; domainCounter++)
            {
                //since the resultsSummary dictionaries don't have names embedded, we can only relate the counts
                //to prize name in context of the associated selection domain.
                SelectionDomain        currentSelectionDomain = selectionDomains[domainCounter];
                IDictionary <int, int> resultsSummary         = resultSummariesList[domainCounter];

                //walk through prizeResultTable one prize at a time, ADDING in counts

                //walk through non zero resultsSummary one prize at a time, linking to prize name and ADDING in counts
                for (int item = 0; item < resultsSummary.Count; item++)
                {
                    if (resultsSummary[item + 1] > 0)
                    {
                        //get prize name from selection domain prizeSelectionTable
                        string prizeName = currentSelectionDomain.PrizeSelectionTable
                                           .Where(r => r.PrizeIndex == (item + 1)).Select(r => r.PrizeName).Single();

                        //now map prizeName back to prizeResultTable
                        PrizeResultRow rowToUpdate = prizeResultTable.Single(p => p.PrizeName == prizeName);

                        rowToUpdate.PrizeSelectedCount += resultsSummary[item + 1];
                    }
                }
            }
            sw.Stop();
            _logger.LogDebug($"finished a selection operation in {sw.ElapsedMilliseconds} milliseconds");

            return(prizeResultTable);
        }