public static IList <P> PartitionSolveAndMergeBack <T, P>(this IList <T> source, Predicate <T> predicate, Func <IList <T>, IList <P> > matchingPartitionSolver, Func <IList <T>, IList <P> > nonMatchingPartitionSolver)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckForNull(predicate, nameof(predicate));
            ArgumentUtility.CheckForNull(matchingPartitionSolver, nameof(matchingPartitionSolver));
            ArgumentUtility.CheckForNull(nonMatchingPartitionSolver, nameof(nonMatchingPartitionSolver));

            var partitionedSource = new PartitionResults <Tuple <int, T> >();

            for (int sourceCnt = 0; sourceCnt < source.Count; sourceCnt++)
            {
                var item = source[sourceCnt];

                if (predicate(item))
                {
                    partitionedSource.MatchingPartition.Add(new Tuple <int, T>(sourceCnt, item));
                }
                else
                {
                    partitionedSource.NonMatchingPartition.Add(new Tuple <int, T>(sourceCnt, item));
                }
            }

            var solvedResult = new List <P>(source.Count);

            if (partitionedSource.MatchingPartition.Any())
            {
                solvedResult.AddRange(matchingPartitionSolver(partitionedSource.MatchingPartition.Select(x => x.Item2).ToList()));
            }

            if (partitionedSource.NonMatchingPartition.Any())
            {
                solvedResult.AddRange(nonMatchingPartitionSolver(partitionedSource.NonMatchingPartition.Select(x => x.Item2).ToList()));
            }

            var result = Enumerable.Repeat(default(P), source.Count).ToList();

            if (solvedResult.Count != source.Count)
            {
                return(solvedResult); // either we can throw here or just return solvedResult and ignore!
            }

            for (int resultCnt = 0; resultCnt < source.Count; resultCnt++)
            {
                if (resultCnt < partitionedSource.MatchingPartition.Count)
                {
                    result[partitionedSource.MatchingPartition[resultCnt].Item1] = solvedResult[resultCnt];
                }
                else
                {
                    result[partitionedSource.NonMatchingPartition[resultCnt - partitionedSource.MatchingPartition.Count].Item1] = solvedResult[resultCnt];
                }
            }

            return(result);
        }
        /// <summary>
        /// Splits an <see cref="IEnumerable{T}"/> into two partitions, determined by the supplied predicate.  Those
        /// that follow the predicate are returned in the first, with the remaining elements in the second.
        /// </summary>
        /// <typeparam name="T">The type of the elements of source.</typeparam>
        /// <param name="source">The source enumerable to partition.</param>
        /// <param name="predicate">The predicate applied to filter the items into their partitions.</param>
        /// <returns>An object containing the matching and nonmatching results.</returns>
        public static PartitionResults <T> Partition <T>(this IEnumerable <T> source, Predicate <T> predicate)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckForNull(predicate, nameof(predicate));

            var results = new PartitionResults <T>();

            foreach (var item in source)
            {
                if (predicate(item))
                {
                    results.MatchingPartition.Add(item);
                }
                else
                {
                    results.NonMatchingPartition.Add(item);
                }
            }

            return(results);
        }