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); } } } } }