/// <inheritdoc />
        public IEnumerable <IList <IDataItem> > Join(IList <IDataProvider> providers, IList <ICollection <DataBinding> > bindingsPerProvider,
                                                     bool includeDynamicItems)
        {
            // Handle degenerate cases.
            int providerCount = providers.Count;

            if (providerCount <= 1)
            {
                return(SequentialJoinStrategy.Instance.Join(providers, bindingsPerProvider, includeDynamicItems));
            }

            // Get all items from all providers.
            var itemLists = new List <IDataItem> [providerCount];

            int[] counts = new int[providerCount];
            for (int i = 0; i < providerCount; i++)
            {
                var items = new List <IDataItem>(providers[i].GetItems(bindingsPerProvider[i], includeDynamicItems));
                int count = items.Count;
                if (count == 0)
                {
                    return(EmptyArray <IList <IDataItem> > .Instance); // there must be at least one value from each provider
                }
                itemLists[i] = items;
                counts[i]    = count;
            }

            var generator = new PairwiseGenerator(counts);

            return(GenerateCombinations(generator, itemLists));
        }
        private static IEnumerable <IList <IDataItem> > GenerateCombinations(PairwiseGenerator generator, List <IDataItem>[] itemLists)
        {
            int providerCount = itemLists.Length;

            int[] indices = new int[providerCount];
            while (generator.Next(indices))
            {
                IDataItem[] combination = new IDataItem[providerCount];
                for (int i = 0; i < providerCount; i++)
                {
                    combination[i] = itemLists[i][indices[i]];
                }

                yield return(combination);
            }
        }
        /// <inheritdoc />
        public IEnumerable<IList<IDataItem>> Join(IList<IDataProvider> providers, IList<ICollection<DataBinding>> bindingsPerProvider,
            bool includeDynamicItems)
        {
            // Handle degenerate cases.
            int providerCount = providers.Count;
            if (providerCount <= 1)
                return SequentialJoinStrategy.Instance.Join(providers, bindingsPerProvider, includeDynamicItems);

            // Get all items from all providers.
            var itemLists = new List<IDataItem>[providerCount];
            int[] counts = new int[providerCount];
            for (int i = 0; i < providerCount; i++)
            {
                var items = new List<IDataItem>(providers[i].GetItems(bindingsPerProvider[i], includeDynamicItems));
                int count = items.Count;
                if (count == 0)
                    return EmptyArray<IList<IDataItem>>.Instance; // there must be at least one value from each provider

                itemLists[i] = items;
                counts[i] = count;
            }

            var generator = new PairwiseGenerator(counts);
            return GenerateCombinations(generator, itemLists);
        }
        private static IEnumerable<IList<IDataItem>> GenerateCombinations(PairwiseGenerator generator, List<IDataItem>[] itemLists)
        {
            int providerCount = itemLists.Length;
            int[] indices = new int[providerCount];
            while (generator.Next(indices))
            {
                IDataItem[] combination = new IDataItem[providerCount];
                for (int i = 0; i < providerCount; i++)
                    combination[i] = itemLists[i][indices[i]];

                yield return combination;
            }
        }