Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/BlameHelper.java @ 680:58a6900f845d
Blame: alternative strategy to handle merge revisions: map(diff(p1->base->p2)) to understand merge intentions better
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Sun, 21 Jul 2013 17:15:34 +0200 |
| parents | 8625cba0a5a8 |
| children |
comparison
equal
deleted
inserted
replaced
| 679:19f5167c2155 | 680:58a6900f845d |
|---|---|
| 17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew; | 19 import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew; |
| 20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; | 20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; |
| 21 | 21 |
| 22 import java.util.ArrayList; | |
| 23 import java.util.Arrays; | |
| 22 import java.util.Iterator; | 24 import java.util.Iterator; |
| 23 import java.util.LinkedList; | 25 import java.util.LinkedList; |
| 26 import java.util.List; | |
| 24 import java.util.ListIterator; | 27 import java.util.ListIterator; |
| 25 | 28 |
| 26 import org.tmatesoft.hg.core.HgCallbackTargetException; | 29 import org.tmatesoft.hg.core.HgCallbackTargetException; |
| 30 import org.tmatesoft.hg.core.Nodeid; | |
| 27 import org.tmatesoft.hg.internal.DiffHelper.LineSequence; | 31 import org.tmatesoft.hg.internal.DiffHelper.LineSequence; |
| 28 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain; | 32 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain; |
| 33 import org.tmatesoft.hg.internal.diff.DiffRangeMap; | |
| 34 import org.tmatesoft.hg.internal.diff.DiffRangeMap.RangePair; | |
| 29 import org.tmatesoft.hg.core.HgBlameInspector; | 35 import org.tmatesoft.hg.core.HgBlameInspector; |
| 30 import org.tmatesoft.hg.core.HgBlameInspector.*; | 36 import org.tmatesoft.hg.core.HgBlameInspector.*; |
| 37 import org.tmatesoft.hg.repo.HgChangelog; | |
| 31 import org.tmatesoft.hg.repo.HgDataFile; | 38 import org.tmatesoft.hg.repo.HgDataFile; |
| 32 import org.tmatesoft.hg.repo.HgInvalidStateException; | 39 import org.tmatesoft.hg.repo.HgInvalidStateException; |
| 40 import org.tmatesoft.hg.repo.HgParentChildMap; | |
| 41 import org.tmatesoft.hg.repo.HgRepository; | |
| 42 import org.tmatesoft.hg.repo.HgRevisionMap; | |
| 33 import org.tmatesoft.hg.repo.HgRuntimeException; | 43 import org.tmatesoft.hg.repo.HgRuntimeException; |
| 34 import org.tmatesoft.hg.util.Adaptable; | 44 import org.tmatesoft.hg.util.Adaptable; |
| 35 import org.tmatesoft.hg.util.CancelledException; | 45 import org.tmatesoft.hg.util.CancelledException; |
| 36 import org.tmatesoft.hg.util.Pair; | 46 import org.tmatesoft.hg.util.Pair; |
| 37 | 47 |
| 43 */ | 53 */ |
| 44 public class BlameHelper { | 54 public class BlameHelper { |
| 45 | 55 |
| 46 private final HgBlameInspector insp; | 56 private final HgBlameInspector insp; |
| 47 private FileLinesCache linesCache; | 57 private FileLinesCache linesCache; |
| 58 private HgParentChildMap<HgChangelog> clogMap; | |
| 48 | 59 |
| 49 public BlameHelper(HgBlameInspector inspector) { | 60 public BlameHelper(HgBlameInspector inspector) { |
| 50 insp = inspector; | 61 insp = inspector; |
| 51 } | 62 } |
| 52 | 63 |
| 98 if (fileParentClogRevs[0] != NO_REVISION && fileParentClogRevs[1] != NO_REVISION) { | 109 if (fileParentClogRevs[0] != NO_REVISION && fileParentClogRevs[1] != NO_REVISION) { |
| 99 int p1ClogIndex = fileParentClogRevs[0]; | 110 int p1ClogIndex = fileParentClogRevs[0]; |
| 100 int p2ClogIndex = fileParentClogRevs[1]; | 111 int p2ClogIndex = fileParentClogRevs[1]; |
| 101 LineSequence p1Lines = linesCache.lines(p1ClogIndex, fileParentRevs[0]); | 112 LineSequence p1Lines = linesCache.lines(p1ClogIndex, fileParentRevs[0]); |
| 102 LineSequence p2Lines = linesCache.lines(p2ClogIndex, fileParentRevs[1]); | 113 LineSequence p2Lines = linesCache.lines(p2ClogIndex, fileParentRevs[1]); |
| 114 MergeResolutionStrategy mergeResolver = createMergeStrategy(fileRevLines, p1Lines, p2Lines, csetRevIndex, fileParentClogRevs); | |
| 115 // | |
| 103 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); | 116 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); |
| 104 pg.init(p2Lines, fileRevLines); | 117 pg.init(p1Lines, fileRevLines); |
| 105 EqualBlocksCollector p2MergeCommon = new EqualBlocksCollector(); | |
| 106 pg.findMatchingBlocks(p2MergeCommon); | |
| 107 // | |
| 108 pg.init(p1Lines); | |
| 109 BlameBlockInspector bbi = new BlameBlockInspector(targetFile, fileRevIndex, insp, p1ClogIndex, csetRevIndex); | 118 BlameBlockInspector bbi = new BlameBlockInspector(targetFile, fileRevIndex, insp, p1ClogIndex, csetRevIndex); |
| 110 bbi.setMergeParent2(new MergeStrategy1(p2MergeCommon.matches), p2ClogIndex); | 119 bbi.setMergeParent2(mergeResolver, p2ClogIndex); |
| 111 pg.findMatchingBlocks(bbi); | 120 pg.findMatchingBlocks(bbi); |
| 112 bbi.checkErrors(); | 121 bbi.checkErrors(); |
| 113 } else if (fileParentClogRevs[0] == fileParentClogRevs[1]) { | 122 } else if (fileParentClogRevs[0] == fileParentClogRevs[1]) { |
| 114 // may be equal iff both are unset | 123 // may be equal iff both are unset |
| 115 assert fileParentClogRevs[0] == NO_REVISION; | 124 assert fileParentClogRevs[0] == NO_REVISION; |
| 130 pg.findMatchingBlocks(bbi); | 139 pg.findMatchingBlocks(bbi); |
| 131 bbi.checkErrors(); | 140 bbi.checkErrors(); |
| 132 } | 141 } |
| 133 } | 142 } |
| 134 | 143 |
| 144 private static final boolean useNewStrategy = Boolean.TRUE.booleanValue(); | |
| 145 | |
| 146 private MergeResolutionStrategy createMergeStrategy(LineSequence fileRevLines, LineSequence p1Lines, LineSequence p2Lines, int csetRevIndex, int[] fileParentClogRevs) { | |
| 147 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); | |
| 148 if (useNewStrategy) { | |
| 149 final ArrayList<RangePairSeq> allMatches = new ArrayList<RangePairSeq>(); | |
| 150 pg.init(p2Lines, fileRevLines); | |
| 151 pg.findAllMatchAlternatives(new DiffHelper.MatchInspector<LineSequence>() { | |
| 152 private RangePairSeq matches; | |
| 153 | |
| 154 public void begin(LineSequence s1, LineSequence s2) { | |
| 155 matches = new RangePairSeq(); | |
| 156 } | |
| 157 | |
| 158 public void match(int startSeq1, int startSeq2, int matchLength) { | |
| 159 matches.add(startSeq1, startSeq2, matchLength); | |
| 160 } | |
| 161 | |
| 162 public void end() { | |
| 163 if (matches.size() > 0) { | |
| 164 allMatches.add(matches); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 }); | |
| 169 // | |
| 170 LineSequence baseLines = getBaseRevisionLines(csetRevIndex, fileParentClogRevs); | |
| 171 pg.init(p1Lines, baseLines); | |
| 172 DiffRangeMap p1ToBase = new DiffRangeMap().fill(pg); | |
| 173 pg.init(baseLines, p2Lines); | |
| 174 DiffRangeMap baseToP2 = new DiffRangeMap().fill(pg); | |
| 175 return new MergeStrategy2(allMatches, p1ToBase, baseToP2); | |
| 176 } else { | |
| 177 pg.init(p2Lines, fileRevLines); | |
| 178 EqualBlocksCollector p2MergeCommon = new EqualBlocksCollector(); | |
| 179 pg.findMatchingBlocks(p2MergeCommon); | |
| 180 return new MergeStrategy1(p2MergeCommon.matches); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 private LineSequence getBaseRevisionLines(int clogRevIndex, int[] fileParentClogRevs) { | |
| 185 assert fileParentClogRevs[0] >= 0; | |
| 186 assert fileParentClogRevs[1] >= 0; | |
| 187 HgDataFile targetFile = linesCache.getFile(clogRevIndex); | |
| 188 final HgRepository repo = targetFile.getRepo(); | |
| 189 if (clogMap == null) { | |
| 190 // FIXME replace HgParentChildMap with revlog.indexWalk(AncestorIterator)) | |
| 191 clogMap = new HgParentChildMap<HgChangelog>(repo.getChangelog()); | |
| 192 clogMap.init(); | |
| 193 } | |
| 194 final HgRevisionMap<HgChangelog> m = clogMap.getRevisionMap(); | |
| 195 Nodeid ancestor = clogMap.ancestor(m.revision(fileParentClogRevs[0]), m.revision(fileParentClogRevs[1])); | |
| 196 final int ancestorRevIndex = m.revisionIndex(ancestor); | |
| 197 Nodeid fr = repo.getManifest().getFileRevision(ancestorRevIndex, targetFile.getPath()); | |
| 198 if (fr == null) { | |
| 199 return LineSequence.newlines(new byte[0]); | |
| 200 } | |
| 201 return linesCache.lines(ancestorRevIndex, targetFile.getRevisionIndex(fr)); | |
| 202 } | |
| 203 | |
| 135 private static class FileLinesCache { | 204 private static class FileLinesCache { |
| 136 private final LinkedList<Pair<Integer, LineSequence>> lruCache; | 205 private final LinkedList<Pair<Integer, LineSequence>> lruCache; |
| 137 private final int limit; | 206 private final int limit; |
| 138 private final LinkedList<Pair<Integer, HgDataFile>> files; // TODO in fact, need sparse array | 207 private final LinkedList<Pair<Integer, HgDataFile>> files; // TODO in fact, need sparse array |
| 139 | 208 |
| 279 if (shallStop()) { | 348 if (shallStop()) { |
| 280 return; | 349 return; |
| 281 } | 350 } |
| 282 try { | 351 try { |
| 283 if (p2MergeCommon != null) { | 352 if (p2MergeCommon != null) { |
| 284 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s2From, s2To - s2From, csetOrigin, csetMergeParent); | 353 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s1From, s1To, s2From, s2To, csetOrigin, csetMergeParent); |
| 285 | 354 |
| 286 /* | 355 /* |
| 287 * Usecases, how it USED TO BE initially: | 356 * Usecases, how it USED TO BE initially: |
| 288 * 3 lines changed to 10 lines. range of 10 lines breaks down to 2 from p2, 3 from p1, and 5 from p2. | 357 * 3 lines changed to 10 lines. range of 10 lines breaks down to 2 from p2, 3 from p1, and 5 from p2. |
| 289 * We report: 2 lines changed to 2(p2), then 1 line changed with 3(p1) and 5 lines added from p2. | 358 * We report: 2 lines changed to 2(p2), then 1 line changed with 3(p1) and 5 lines added from p2. |
| 343 if (shallStop()) { | 412 if (shallStop()) { |
| 344 return; | 413 return; |
| 345 } | 414 } |
| 346 try { | 415 try { |
| 347 if (p2MergeCommon != null) { | 416 if (p2MergeCommon != null) { |
| 348 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s2From, s2To - s2From, csetOrigin, csetMergeParent); | 417 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s1InsertPoint, s2From, s2To, csetOrigin, csetMergeParent); |
| 349 int insPoint = s1InsertPoint; // track changes to insertion point | 418 int insPoint = s1InsertPoint; // track changes to insertion point |
| 350 for (IntTuple mergeRange : mergeRanges) { | 419 for (IntTuple mergeRange : mergeRanges) { |
| 351 int rangeOrigin = mergeRange.at(0); | 420 int rangeOrigin = mergeRange.at(0); |
| 352 int rangeStart = mergeRange.at(1); | 421 int rangeStart = mergeRange.at(1); |
| 353 int rangeLen = mergeRange.at(2); | 422 int rangeLen = mergeRange.at(2); |
| 615 | 684 |
| 616 public byte[] asArray() { | 685 public byte[] asArray() { |
| 617 return contentBlock.seq.data(from, from + length); | 686 return contentBlock.seq.data(from, from + length); |
| 618 } | 687 } |
| 619 } | 688 } |
| 620 | 689 |
| 621 | |
| 622 interface MergeResolutionStrategy { | |
| 623 /** | |
| 624 * breaks region [start..start+length) into ranges according to deduced (or simply guessed) | |
| 625 * matching of these lines to lines in source1 and source2 | |
| 626 * @return list of tuples (source, start, length), where source is one of the identifiers supplied | |
| 627 */ | |
| 628 public IntSliceSeq combineAndMarkRangesWithSource(int start, int length, int source1, int source2); | |
| 629 public int getLineInP2(int mergeLine); | |
| 630 } | |
| 631 | |
| 632 private static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> { | 690 private static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> { |
| 633 private final RangePairSeq matches = new RangePairSeq(); | 691 private final RangePairSeq matches = new RangePairSeq(); |
| 634 | 692 |
| 635 public void begin(LineSequence s1, LineSequence s2) { | 693 public void begin(LineSequence s1, LineSequence s2) { |
| 636 } | 694 } |
| 658 result.add((start + length) - s); | 716 result.add((start + length) - s); |
| 659 } | 717 } |
| 660 } | 718 } |
| 661 | 719 |
| 662 } | 720 } |
| 663 | 721 |
| 722 interface MergeResolutionStrategy { | |
| 723 /** | |
| 724 * breaks region [start2..end2) into ranges according to deduced (or simply guessed) | |
| 725 * matching of [start1..end1) lines to lines in source1 and source2 | |
| 726 * @return list of tuples (source, start, length), where source is one of the identifiers supplied | |
| 727 */ | |
| 728 public IntSliceSeq combineAndMarkRangesWithSource(int start1, int end1, int start2, int end2, int source1, int source2); | |
| 729 public IntSliceSeq combineAndMarkRangesWithSource(int insPoint, int start, int end, int source1, int source2); | |
| 730 public int getLineInP2(int mergeLine); | |
| 731 } | |
| 732 | |
| 733 // report lines as merged from p2 solely based on whether target line belongs | |
| 734 // to a region that is equal to p2 region | |
| 664 private static class MergeStrategy1 implements MergeResolutionStrategy { | 735 private static class MergeStrategy1 implements MergeResolutionStrategy { |
| 665 // equal ranges in p2 and merged revision | 736 // equal ranges in p2 and merged revision |
| 666 private final RangePairSeq matches; | 737 private final RangePairSeq matches; |
| 667 private final IntSliceSeq mergeRanges; | 738 private final IntSliceSeq mergeRanges; |
| 668 | 739 |
| 675 * intersects [start..start+length) with ranges of target lines, and based on the intersection | 746 * intersects [start..start+length) with ranges of target lines, and based on the intersection |
| 676 * breaks initial range into smaller ranges and records them into result, with marker to indicate | 747 * breaks initial range into smaller ranges and records them into result, with marker to indicate |
| 677 * whether the range is from initial range (markerSource) or is a result of the intersection with target | 748 * whether the range is from initial range (markerSource) or is a result of the intersection with target |
| 678 * (markerTarget) | 749 * (markerTarget) |
| 679 */ | 750 */ |
| 680 public IntSliceSeq combineAndMarkRangesWithSource(int start, int length, int markerSource, int markerTarget) { | 751 private IntSliceSeq doCombine(int start, int length, int markerSource, int markerTarget) { |
| 681 mergeRanges.clear(); | 752 mergeRanges.clear(); |
| 682 assert mergeRanges.sliceSize() == 3; | 753 assert mergeRanges.sliceSize() == 3; |
| 683 int sourceStart = start, targetStart = start, sourceEnd = start + length; | 754 int sourceStart = start, targetStart = start, sourceEnd = start + length; |
| 684 for (int l = sourceStart; l < sourceEnd; l++) { | 755 for (int l = sourceStart; l < sourceEnd; l++) { |
| 685 if (matches.includesTargetLine(l)) { | 756 if (matches.includesTargetLine(l)) { |
| 714 } | 785 } |
| 715 | 786 |
| 716 public int getLineInP2(int mergeLine) { | 787 public int getLineInP2(int mergeLine) { |
| 717 return matches.reverseMapLine(mergeLine); | 788 return matches.reverseMapLine(mergeLine); |
| 718 } | 789 } |
| 719 } | 790 |
| 791 public IntSliceSeq combineAndMarkRangesWithSource(int start1, int end1, int start2, int end2, int source1, int source2) { | |
| 792 return doCombine(start2, end2 - start2, source1, source2); | |
| 793 } | |
| 794 | |
| 795 public IntSliceSeq combineAndMarkRangesWithSource(int insPoint, int start, int end, int source1, int source2) { | |
| 796 return doCombine(start, end - start, source1, source2); | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 private static class MergeStrategy2 implements MergeResolutionStrategy { | |
| 801 // equal ranges in p2 and merged revision | |
| 802 private final List<RangePairSeq> matches; | |
| 803 private final IntSliceSeq mergeRanges; | |
| 804 private final DiffRangeMap p1ToBase; | |
| 805 private final DiffRangeMap baseToP2; | |
| 806 | |
| 807 public MergeStrategy2(List<RangePairSeq> p2EqualToM, DiffRangeMap p1ToBaseRanges, DiffRangeMap baseToP2Ranges) { | |
| 808 matches = p2EqualToM; | |
| 809 p1ToBase = p1ToBaseRanges; | |
| 810 baseToP2= baseToP2Ranges; | |
| 811 mergeRanges = new IntSliceSeq(3, 10, 10); | |
| 812 } | |
| 813 | |
| 814 | |
| 815 public IntSliceSeq combineAndMarkRangesWithSource(int insPoint, int start, int end, int source1, int source2) { | |
| 816 return combineAndMarkRangesWithSource(insPoint, insPoint, start, end, source1, source2); | |
| 817 } | |
| 818 | |
| 819 public IntSliceSeq combineAndMarkRangesWithSource(int start1, int end1, int start2, int end2, int source1, int source2) { | |
| 820 mergeRanges.clear(); | |
| 821 IntSliceSeq mergedLines = new IntSliceSeq(2, end2-start2, 0); | |
| 822 for (int i = start2; i < end2; i++) { | |
| 823 mergedLines.add(source1, 0); | |
| 824 } | |
| 825 // [s1Start..s1End) // range in p1 seen as changed in m | |
| 826 for (RangePair p1_b : p1ToBase.findInSource(start1, end1)) { | |
| 827 // there might be few ranges in (p1-base) that overlap with (p1-m) changes | |
| 828 for (RangePair b_p2 : baseToP2.findInSource(p1_b.start2(), p1_b.end2())) { | |
| 829 // regions in p2 that correspond to affected regions in base | |
| 830 for (int p2Line = b_p2.start2(); p2Line < b_p2.end2(); p2Line++) { | |
| 831 for (RangePairSeq eq : matches) { | |
| 832 if (eq.includesOriginLine(p2Line)) { | |
| 833 // this line in p2 is equal to some line in merge | |
| 834 int mergeLine = eq.mapLineIndex(p2Line); | |
| 835 if (mergeLine >= start2 && mergeLine < end2) { | |
| 836 mergedLines.set(mergeLine - start2, source2, p2Line); | |
| 837 } | |
| 838 } | |
| 839 } | |
| 840 } | |
| 841 } | |
| 842 } | |
| 843 int lineCount = 0, start = start2; | |
| 844 int lastSeenSource = source1; | |
| 845 for (IntTuple t : mergedLines) { | |
| 846 if (t.at(0) == lastSeenSource) { | |
| 847 lineCount++; | |
| 848 } else { | |
| 849 if (lineCount > 0) { | |
| 850 mergeRanges.add(lastSeenSource, start, lineCount); | |
| 851 start += lineCount; | |
| 852 } | |
| 853 lineCount = 1; | |
| 854 lastSeenSource = t.at(0); | |
| 855 } | |
| 856 } | |
| 857 if (lineCount > 0) { | |
| 858 mergeRanges.add(lastSeenSource, start, lineCount); | |
| 859 } | |
| 860 return mergeRanges; | |
| 861 } | |
| 862 | |
| 863 public int getLineInP2(int mergeLine) { | |
| 864 for (RangePairSeq eq : matches) { | |
| 865 if (eq.includesTargetLine(mergeLine)) { | |
| 866 return eq.reverseMapLine(mergeLine); | |
| 867 } | |
| 868 } | |
| 869 return -1; | |
| 870 } | |
| 871 } | |
| 872 | |
| 720 | 873 |
| 721 private static class AnnotateRev implements RevisionDescriptor { | 874 private static class AnnotateRev implements RevisionDescriptor { |
| 722 public ContentBlock origin, target; | 875 public ContentBlock origin, target; |
| 723 public int originCset, targetCset, mergeCset, fileRevIndex; | 876 public int originCset, targetCset, mergeCset, fileRevIndex; |
| 724 public HgDataFile df; | 877 public HgDataFile df; |
| 787 for (int i = 0; i < r.size(); i+=2) { | 940 for (int i = 0; i < r.size(); i+=2) { |
| 788 System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1)); | 941 System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1)); |
| 789 } | 942 } |
| 790 System.out.println(); | 943 System.out.println(); |
| 791 MergeStrategy1 ms = new MergeStrategy1(bc.matches); | 944 MergeStrategy1 ms = new MergeStrategy1(bc.matches); |
| 792 IntSliceSeq mr = ms.combineAndMarkRangesWithSource(0, 16, 508, 514); | 945 IntSliceSeq mr = ms.doCombine(0, 16, 508, 514); |
| 793 for (IntTuple t : mr) { | 946 for (IntTuple t : mr) { |
| 794 System.out.printf("%d:[%d..%d) ", t.at(0), t.at(1), t.at(1) + t.at(2)); | 947 System.out.printf("%d:[%d..%d) ", t.at(0), t.at(1), t.at(1) + t.at(2)); |
| 795 } | 948 } |
| 949 System.out.println(); | |
| 950 System.out.println(); | |
| 951 DiffRangeMap m1 = new DiffRangeMap(); // p1 -> base | |
| 952 m1.match(0, 0, 1); // =1..1 -> 1..1 | |
| 953 m1.match(7, 3, 0); // *2..7 -> 2..3 | |
| 954 DiffRangeMap m2 = new DiffRangeMap(); // base -> p2 | |
| 955 m2.match(0, 0, 1); // =1..1 -> 1..1 | |
| 956 m2.match(3, 3, 0); // *2..3 -> 2..3 | |
| 957 RangePairSeq eq1 = new RangePairSeq(); | |
| 958 eq1.add(0, 0, 3); | |
| 959 RangePairSeq eq2 = new RangePairSeq(); | |
| 960 eq2.add(0, 4, 3); | |
| 961 MergeStrategy2 ms2 = new MergeStrategy2(Arrays.asList(eq1, eq2), m1, m2); | |
| 962 mr = ms2.combineAndMarkRangesWithSource(5, 7, 5, 7, 33, 44); | |
| 963 for (IntTuple t : mr) { | |
| 964 System.out.printf("%d:[%d..%d) ", t.at(0), t.at(1), t.at(1) + t.at(2)); | |
| 965 } | |
| 796 } | 966 } |
| 797 } | 967 } |
