private static UsedRangeList <TLetter> MakeRanges(IDictionary <Id <RxMatch <TLetter> >, KeyValuePair <RangeSet <TLetter>, ICollection <LetterId> > > charsets, RangeSet <TLetter> validRanges)
        {
            var ranges = new UsedRangeList <TLetter>();

            foreach (var validRange in validRanges)
            {
                ranges.Add(new UsedLetterRange <TLetter>(validRange, null));
            }
            foreach (var pair in charsets)
            {
                foreach (var charRange in pair.Value.Key)
                {
                    // split left if necessary
                    var left = RangeOperations <TLetter> .BinarySearch(ranges, charRange.From);

                    var leftRange = ranges[left];
                    if (leftRange.From.CompareTo(charRange.From) < 0)
                    {
                        ranges.Insert(left++, new UsedLetterRange <TLetter>(leftRange.From, Incrementor <TLetter> .Decrement(charRange.From), leftRange.Users));
                        ranges[left] = new UsedLetterRange <TLetter>(charRange.From, leftRange.To, leftRange.Users);
                    }
                    // split right if necessary
                    var right = RangeOperations <TLetter> .BinarySearch(ranges, charRange.To);

                    var rightRange = ranges[right];
                    if (rightRange.To.CompareTo(charRange.To) > 0)
                    {
                        ranges[right] = new UsedLetterRange <TLetter>(rightRange.From, charRange.To, rightRange.Users);
                        ranges.Insert(right + 1, new UsedLetterRange <TLetter>(Incrementor <TLetter> .Increment(charRange.To), rightRange.To, rightRange.Users));
                    }
                    // add user information
                    for (var i = left; i <= right; i++)
                    {
                        ranges[i] = ranges[i].AddUser(pair.Key);
                    }
                }
            }
            return(ranges);
        }
        /// <summary>
        ///     Enumerates all ranges of left and right combined, and call the <paramref name="process" /> callback for each range,
        ///     passing the index into the origin left and/or right range set.
        /// </summary>
        /// <typeparam name="TRangeSetLeft">Type of the left range set.</typeparam>
        /// <typeparam name="TRangeSetRight">Type of the right range set.</typeparam>
        /// <typeparam name="TResult">Type of the result.</typeparam>
        /// <param name="left">The left <see cref="RangeSet{T}" />.</param>
        /// <param name="right">The right <see cref="RangeSet{T}" />.</param>
        /// <param name="process">The process.</param>
        /// <returns>An enumerator that allows foreach to be used to process the ranges in this collection.</returns>
        public static IEnumerable <TResult> EnumerateRanges <TRangeSetLeft, TRangeSetRight, TResult>(TRangeSetLeft left, TRangeSetRight right, Func <Range <T>, int?, int?, TResult> process)
            where TRangeSetLeft : IRangeSet <T>
            where TRangeSetRight : IRangeSet <T>
        {
            using (var enumLeft = left == null ? Enumerable.Empty <Range <T> >().GetEnumerator() : left.GetEnumerator()) {
                using (var enumRight = right == null ? Enumerable.Empty <Range <T> >().GetEnumerator() : right.GetEnumerator()) {
                    var ixLeft   = -1;
                    var ixRight  = -1;
                    var rngLeft  = enumLeft.GetNext(ref ixLeft);
                    var rngRight = enumRight.GetNext(ref ixRight);
                    while (rngLeft.HasValue || rngRight.HasValue)
                    {
                        if (rngLeft.HasValue && (!rngRight.HasValue || (rngLeft.Value.To.CompareTo(rngRight.Value.From) < 0)))
                        {
                            // no overlap, only in this
                            yield return(process(rngLeft.Value, ixLeft, null));

                            rngLeft = enumLeft.GetNext(ref ixLeft);
                            continue;
                        }
                        // if we get here then rngOther.HasValue == true
                        if (!rngLeft.HasValue || (rngRight.Value.To.CompareTo(rngLeft.Value.From) < 0))
                        {
                            // no overlap, only in other
                            yield return(process(rngRight.Value, null, ixRight));

                            rngRight = enumRight.GetNext(ref ixRight);
                            continue;
                        }
                        // if we get here then we have an overlap. first return any overhang on the "from" side
                        if (rngLeft.Value.From.CompareTo(rngRight.Value.From) < 0)
                        {
                            Debug.Assert(rngLeft.Value.To.CompareTo(rngRight.Value.From) >= 0);
                            yield return(process(new Range <T>(rngLeft.Value.From, Incrementor <T> .Decrement(rngRight.Value.From)), ixLeft, null));

                            rngLeft = new Range <T>(rngRight.Value.From, rngLeft.Value.To);
                        }
                        else if (rngLeft.Value.From.CompareTo(rngRight.Value.From) > 0)
                        {
                            Debug.Assert(rngRight.Value.To.CompareTo(rngLeft.Value.From) >= 0);
                            yield return(process(new Range <T>(rngRight.Value.From, Incrementor <T> .Decrement(rngLeft.Value.From)), null, ixRight));

                            rngRight = new Range <T>(rngLeft.Value.From, rngRight.Value.To);
                        }
                        // next return overlapping part and fixup ranges for next iteration
                        Debug.Assert(rngLeft.Value.From.CompareTo(rngRight.Value.From) == 0);
                        if (rngLeft.Value.To.CompareTo(rngRight.Value.To) < 0)
                        {
                            // rngOther is longer
                            yield return(process(rngLeft.Value, ixLeft, ixRight));

                            rngRight = new Range <T>(Incrementor <T> .Increment(rngLeft.Value.To), rngRight.Value.To);
                            rngLeft  = enumLeft.GetNext(ref ixLeft);
                        }
                        else if (rngLeft.Value.To.CompareTo(rngRight.Value.To) > 0)
                        {
                            // rngThis is longer
                            yield return(process(rngRight.Value, ixLeft, ixRight));

                            rngLeft  = new Range <T>(Incrementor <T> .Increment(rngRight.Value.To), rngLeft.Value.To);
                            rngRight = enumRight.GetNext(ref ixRight);
                        }
                        else
                        {
                            // both equal
                            yield return(process(rngLeft.Value, ixLeft, ixRight));

                            rngLeft  = enumLeft.GetNext(ref ixLeft);
                            rngRight = enumRight.GetNext(ref ixRight);
                        }
                    }
                }
            }
        }