changeset 674:cce0387c6041

Introduced dedicated IntSliceSeq/IntTuple in place of IntArray with subsequences
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 17 Jul 2013 15:40:51 +0200
parents 545b1d4cc11d
children a20121a2bba6
files src/org/tmatesoft/hg/internal/BlameHelper.java src/org/tmatesoft/hg/internal/FileAnnotation.java src/org/tmatesoft/hg/internal/IntSliceSeq.java src/org/tmatesoft/hg/internal/IntTuple.java src/org/tmatesoft/hg/internal/RangePairSeq.java src/org/tmatesoft/hg/internal/RangeSeq.java test/org/tmatesoft/hg/test/TestAuxUtilities.java
diffstat 7 files changed, 411 insertions(+), 216 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/BlameHelper.java	Fri Jul 12 20:14:24 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/BlameHelper.java	Wed Jul 17 15:40:51 2013 +0200
@@ -19,6 +19,7 @@
 import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew;
 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
 
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.ListIterator;
 
@@ -219,7 +220,7 @@
 		private final int csetTarget;
 		private EqualBlocksCollector p2MergeCommon;
 		private int csetMergeParent;
-		private IntVector mergeRanges;
+		private IntSliceSeq mergeRanges;
 		private final AnnotateRev annotatedRevision;
 		private HgCallbackTargetException error;
 
@@ -235,7 +236,7 @@
 		public void setMergeParent2(EqualBlocksCollector p2Merge, int parentCset2) {
 			p2MergeCommon = p2Merge;
 			csetMergeParent = parentCset2;
-			mergeRanges = new IntVector(3*10, 3*10);
+			mergeRanges = new IntSliceSeq(3, 10, 10);
 		}
 		
 		@Override
@@ -298,11 +299,12 @@
 					 */
 					int s1TotalLines = s1To - s1From, s1ConsumedLines = 0, s1Start = s1From;
 					
-					for (int i = 0; i < mergeRanges.size(); i += 3) {
-						final int rangeOrigin = mergeRanges.get(i);
-						final int rangeStart = mergeRanges.get(i+1);
-						final int rangeLen = mergeRanges.get(i+2);
-						final boolean lastRange = i+3 >= mergeRanges.size();
+					for (Iterator<IntTuple> it = mergeRanges.iterator(); it.hasNext();) {
+						IntTuple mergeRange = it.next();
+						final int rangeOrigin = mergeRange.at(0);
+						final int rangeStart = mergeRange.at(1);
+						final int rangeLen = mergeRange.at(2);
+						final boolean lastRange = it.hasNext();
 						final int s1LinesLeft = s1TotalLines - s1ConsumedLines;
 						// how many lines we may report as changed (don't use more than in range unless it's the very last range)
 						final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen);
@@ -349,10 +351,10 @@
 					mergeRanges.clear();
 					p2MergeCommon.combineAndMarkRangesWithTarget(s2From, s2To - s2From, csetOrigin, csetMergeParent, mergeRanges);
 					int insPoint = s1InsertPoint; // track changes to insertion point
-					for (int i = 0; i < mergeRanges.size(); i += 3) {
-						int rangeOrigin = mergeRanges.get(i);
-						int rangeStart = mergeRanges.get(i+1);
-						int rangeLen = mergeRanges.get(i+2);
+					for (IntTuple mergeRange : mergeRanges) {
+						int rangeOrigin = mergeRange.at(0);
+						int rangeStart = mergeRange.at(1);
+						int rangeLen = mergeRange.at(2);
 						ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, insPoint);
 						block.setOriginAndTarget(rangeOrigin, csetTarget);
 						insp.added(block);
@@ -619,7 +621,7 @@
 	
 
 	private static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> {
-		private final RangeSeq matches = new RangeSeq();
+		private final RangePairSeq matches = new RangePairSeq();
 
 		public void begin(LineSequence s1, LineSequence s2) {
 		}
@@ -658,16 +660,15 @@
 		 * whether the range is from initial range (markerSource) or is a result of the intersection with target
 		 * (markerTarget)
 		 */
-		public void combineAndMarkRangesWithTarget(int start, int length, int markerSource, int markerTarget, IntVector result) {
+		public void combineAndMarkRangesWithTarget(int start, int length, int markerSource, int markerTarget, IntSliceSeq result) {
+			assert result.sliceSize() == 3;
 			int sourceStart = start, targetStart = start, sourceEnd = start + length;
 			for (int l = sourceStart; l < sourceEnd; l++) {
 				if (matches.includesTargetLine(l)) {
 					// l is from target
 					if (sourceStart < l) {
 						// few lines from source range were not in the target, report them
-						result.add(markerSource);
-						result.add(sourceStart);
-						result.add(l - sourceStart);
+						result.add(markerSource, sourceStart, l - sourceStart);
 					}
 					// indicate the earliest line from source range to use
 					sourceStart = l + 1;
@@ -675,9 +676,7 @@
 					// l is not in target
 					if (targetStart < l) {
 						// report lines from target range
-						result.add(markerTarget);
-						result.add(targetStart);
-						result.add(l - targetStart);
+						result.add(markerTarget, targetStart, l - targetStart);
 					}
 					// next line *may* be from target
 					targetStart = l + 1;
@@ -688,14 +687,10 @@
 			if (sourceStart < sourceEnd) {
 				assert targetStart == sourceEnd;
 				// something left from the source range
-				result.add(markerSource);
-				result.add(sourceStart);
-				result.add(sourceEnd - sourceStart);
+				result.add(markerSource, sourceStart, sourceEnd - sourceStart);
 			} else if (targetStart < sourceEnd) {
 				assert sourceStart == sourceEnd;
-				result.add(markerTarget);
-				result.add(targetStart);
-				result.add(sourceEnd - targetStart);
+				result.add(markerTarget, targetStart, sourceEnd - targetStart);
 			}
 		}
 	}
@@ -770,10 +765,10 @@
 			System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1));
 		}
 		System.out.println();
-		r.clear();
-		bc.combineAndMarkRangesWithTarget(0, 16, 508, 514, r);
-		for (int i = 0; i < r.size(); i+=3) {
-			System.out.printf("%d:[%d..%d)  ", r.get(i), r.get(i+1), r.get(i+1) + r.get(i+2));
+		IntSliceSeq mr = new IntSliceSeq(3);
+		bc.combineAndMarkRangesWithTarget(0, 16, 508, 514, mr);
+		for (IntTuple t : mr) {
+			System.out.printf("%d:[%d..%d)  ", t.at(0), t.at(1), t.at(1) + t.at(2));
 		}
 	}
 }
--- a/src/org/tmatesoft/hg/internal/FileAnnotation.java	Fri Jul 12 20:14:24 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java	Wed Jul 17 15:40:51 2013 +0200
@@ -43,18 +43,18 @@
 	}
 
 	// keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from some previous step
-	private RangeSeq activeEquals;
+	private RangePairSeq activeEquals;
 	// equal blocks of the current iteration, to be recalculated before next step
 	// to track line number (current target to ultimate target) mapping 
-	private RangeSeq intermediateEquals = new RangeSeq();
+	private RangePairSeq intermediateEquals = new RangePairSeq();
 
 	private boolean[] knownLines;
 	private final LineInspector delegate;
 	private RevisionDescriptor revisionDescriptor;
 	private BlockData lineContent;
 
-	private IntMap<RangeSeq> mergedRanges = new IntMap<RangeSeq>(10);
-	private IntMap<RangeSeq> equalRanges = new IntMap<RangeSeq>(10);
+	private IntMap<RangePairSeq> mergedRanges = new IntMap<RangePairSeq>(10);
+	private IntMap<RangePairSeq> equalRanges = new IntMap<RangePairSeq>(10);
 	private boolean activeEqualsComesFromMerge = false;
 
 	public FileAnnotation(LineInspector lineInspector) {
@@ -66,7 +66,7 @@
 		if (knownLines == null) {
 			lineContent = rd.target();
 			knownLines = new boolean[lineContent.elementCount()];
-			activeEquals = new RangeSeq();
+			activeEquals = new RangePairSeq();
 			activeEquals.add(0, 0, knownLines.length);
 			equalRanges.put(rd.targetChangesetIndex(), activeEquals);
 		} else {
@@ -85,7 +85,7 @@
 
 	public void done(RevisionDescriptor rd) {
 		// update line numbers of the intermediate target to point to ultimate target's line numbers
-		RangeSeq v = intermediateEquals.intersect(activeEquals);
+		RangePairSeq v = intermediateEquals.intersect(activeEquals);
 		if (activeEqualsComesFromMerge) {
 			mergedRanges.put(rd.originChangesetIndex(), v);
 		} else {
@@ -94,7 +94,7 @@
 		if (rd.isMerge() && !mergedRanges.containsKey(rd.mergeChangesetIndex())) {
 			// seen merge, but no lines were merged from p2.
 			// Add empty range to avoid uncertainty when a parent of p2 pops in
-			mergedRanges.put(rd.mergeChangesetIndex(), new RangeSeq());
+			mergedRanges.put(rd.mergeChangesetIndex(), new RangePairSeq());
 		}
 		intermediateEquals.clear();
 		activeEquals = null;
@@ -107,11 +107,11 @@
 	}
 
 	public void added(AddBlock block) {
-		RangeSeq rs = null;
+		RangePairSeq rs = null;
 		if (revisionDescriptor.isMerge() && block.originChangesetIndex() == revisionDescriptor.mergeChangesetIndex()) {
 			rs = mergedRanges.get(revisionDescriptor.mergeChangesetIndex());
 			if (rs == null) {
-				mergedRanges.put(revisionDescriptor.mergeChangesetIndex(), rs = new RangeSeq());
+				mergedRanges.put(revisionDescriptor.mergeChangesetIndex(), rs = new RangePairSeq());
 			}
 		}
 		if (activeEquals.size() == 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/IntSliceSeq.java	Wed Jul 17 15:40:51 2013 +0200
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2013 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.internal;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public final class IntSliceSeq implements Iterable<IntTuple> {
+	private final IntVector slices;
+	private final int slice;
+
+	public IntSliceSeq(int sliceSize) {
+		// initial size/grow values are pure guess
+		this(sliceSize, 10, 5);
+	}
+	
+	public IntSliceSeq(int sliceSize, int initialSlices, int slicesToGrow) {
+		slices = new IntVector(sliceSize * initialSlices, sliceSize*slicesToGrow);
+		slice = sliceSize;
+	}
+	
+	public IntSliceSeq add(int... values) {
+		checkValues(values);
+		slices.add(values);
+		return this;
+	}
+	
+	public IntSliceSeq set(int sliceIndex, int... values) {
+		checkValues(values);
+		for (int i = 0, j = sliceIndex*slice; i < slice; i++,j++) {
+			slices.set(j, values[i]);
+		}
+		return this;
+	}
+	
+	public IntTuple get(int sliceIndex) {
+		checkArgRange(size(), sliceIndex);
+		return new IntTuple(slice).set(slices, sliceIndex*slice);
+	}
+	
+	public int get(int sliceIndex, int valueIndex) {
+		checkArgRange(size(), sliceIndex);
+		checkArgRange(slice, valueIndex);
+		return slices.get(sliceIndex*slice + valueIndex);
+	}
+	
+	public int size() {
+		return slices.size() / slice;
+	}
+	
+	public int sliceSize() {
+		return slice;
+	}
+
+	public void clear() {
+		slices.clear();
+	}
+
+	public IntTuple last() {
+		int lastElementIndex = (size() - 1);
+		if (lastElementIndex < 0) {
+			throw new NoSuchElementException();
+		}
+		return get(lastElementIndex);
+	}
+
+	public Iterator<IntTuple> iterator() {
+		return new Iterator<IntTuple>() {
+			private final IntTuple t = new IntTuple(slice);
+			private int next = 0;
+
+			public boolean hasNext() {
+				return next < size();
+			}
+
+			public IntTuple next() {
+				return t.set(slices, next++*slice);
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	private void checkArgRange(int rangeSize, int index) {
+		if (index >= 0 && index < rangeSize) {
+			return;
+		}
+		throw new IllegalArgumentException(String.valueOf(index));
+	}
+	private void checkValues(int[] values) {
+		if (values == null || values.length != slice) {
+			throw new IllegalArgumentException(String.valueOf(values == null ? values : values.length));
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/IntTuple.java	Wed Jul 17 15:40:51 2013 +0200
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.internal;
+
+/**
+ * Tuple of integers
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public final class IntTuple implements Cloneable {
+	private final int size;
+	private IntVector v;
+	private int start;
+
+	IntTuple(int length) {
+		size = length;
+	}
+	/*package*/IntTuple set(IntVector vect, int index) {
+		v = vect;
+		start = index;
+		return this;
+	}
+	
+	public int size() {
+		return size;
+	}
+
+	public int at(int index) {
+		if (index < size) {
+			return v.get(start + index);
+		}
+		throw new IllegalArgumentException(String.valueOf(index));
+	}
+	
+	public IntTuple clone() {
+		try {
+			return (IntTuple) super.clone();
+		} catch (CloneNotSupportedException ex) {
+			throw new Error(ex);
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/RangePairSeq.java	Wed Jul 17 15:40:51 2013 +0200
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.internal;
+
+import java.util.Formatter;
+
+/**
+ * Sequence of range pairs (denoted origin and target), {originStart, targetStart, length}, tailored for diff/annotate
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public final class RangePairSeq {
+	private final IntSliceSeq ranges = new IntSliceSeq(3);
+	
+	public void add(int start1, int start2, int length) {
+		int count = ranges.size();
+		if (count > 0) {
+			int lastS1 = ranges.get(--count, 0);
+			int lastS2 = ranges.get(count, 1);
+			int lastLen = ranges.get(count, 2);
+			if (start1 == lastS1 + lastLen && start2 == lastS2 + lastLen) {
+				// new range continues the previous one - just increase the length
+				ranges.set(count, lastS1, lastS2, lastLen + length);
+				return;
+			}
+		}
+		ranges.add(start1, start2, length);
+	}
+	
+	public void clear() {
+		ranges.clear();
+	}
+
+	public int size() {
+		return ranges.size();
+	}
+
+	/**
+	 * find out line index in the target that matches specified origin line
+	 */
+	public int mapLineIndex(int ln) {
+		for (IntTuple t : ranges) {
+			int s1 = t.at(0);
+			if (s1 > ln) {
+				return -1;
+			}
+			int l = t.at(2);
+			if (s1 + l > ln) {
+				int s2 = t.at(1);
+				return s2 + (ln - s1);
+			}
+		}
+		return -1;
+	}
+	
+	/**
+	 * find out line index in origin that matches specified target line
+	 */
+	public int reverseMapLine(int targetLine) {
+		for (IntTuple t : ranges) {
+			int ts = t.at(1);
+			if (ts > targetLine) {
+				return -1;
+			}
+			int l = t.at(2);
+			if (ts + l > targetLine) {
+				int os = t.at(0);
+				return os + (targetLine - ts);
+			}
+		}
+		return -1;
+	}
+	
+	public RangePairSeq intersect(RangePairSeq target) {
+		RangePairSeq v = new RangePairSeq();
+		for (IntTuple t : ranges) {
+			int originLine = t.at(0);
+			int targetLine = t.at(1);
+			int length = t.at(2);
+			int startTargetLine = -1, startOriginLine = -1, c = 0;
+			for (int j = 0; j < length; j++) {
+				int lnInFinal = target.mapLineIndex(targetLine + j);
+				if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) {
+					// the line is not among "same" in ultimate origin
+					// or belongs to another/next "same" chunk 
+					if (startOriginLine == -1) {
+						continue;
+					}
+					v.add(startOriginLine, startTargetLine, c);
+					c = 0;
+					startOriginLine = startTargetLine = -1;
+					// fall-through to check if it's not complete miss but a next chunk
+				}
+				if (lnInFinal != -1) {
+					if (startOriginLine == -1) {
+						startOriginLine = originLine + j;
+						startTargetLine = lnInFinal;
+						c = 1;
+					} else {
+						// lnInFinal != startTargetLine + s is covered above
+						assert lnInFinal == startTargetLine + c;
+						c++;
+					}
+				}
+			}
+			if (startOriginLine != -1) {
+				assert c > 0;
+				v.add(startOriginLine, startTargetLine, c);
+			}
+		}
+		return v;
+	}
+	
+	// true when specified line in origin is equal to a line in target
+	public boolean includesOriginLine(int ln) {
+		return includes(ln, 0);
+	}
+	
+	// true when specified line in target is equal to a line in origin
+	public boolean includesTargetLine(int ln) {
+		return includes(ln, 1);
+	}
+
+	private boolean includes(int ln, int o) {
+		for (IntTuple t : ranges) {
+			int rangeStart = t.at(o);
+			if (rangeStart > ln) {
+				return false;
+			}
+			int rangeLen = t.at(2);
+			if (rangeStart + rangeLen > ln) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public CharSequence dump() {
+		StringBuilder sb = new StringBuilder();
+		Formatter f = new Formatter(sb);
+		for (IntTuple t : ranges) {
+			int s1 = t.at(0);
+			int s2 = t.at(1);
+			int len = t.at(2);
+			f.format("[%d..%d) == [%d..%d);  ", s1, s1 + len, s2, s2 + len);
+		}
+		return sb;
+	}
+	
+	@Override
+	public String toString() {
+		return String.format("RangeSeq[%d]:%s", size(), dump());
+	}
+}
\ No newline at end of file
--- a/src/org/tmatesoft/hg/internal/RangeSeq.java	Fri Jul 12 20:14:24 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-/*
- * Copyright (c) 2013 TMate Software Ltd
- *  
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * For information on how to redistribute this software under
- * the terms of a license other than GNU General Public License
- * contact TMate Software at support@hg4j.com
- */
-package org.tmatesoft.hg.internal;
-
-import java.util.Formatter;
-
-/**
- * Sequence of range pairs (denoted origin and target), {originStart, targetStart, length}, tailored for diff/annotate
- * 
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public final class RangeSeq {
-	// XXX smth like IntSliceVector to access triples (or slices of any size, in fact)
-	// with easy indexing, e.g. #get(sliceIndex, indexWithinSlice)
-	// and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2)
-	private final IntVector ranges = new IntVector(3*10, 3*5);
-	private int count;
-	
-	public void add(int start1, int start2, int length) {
-		if (count > 0) {
-			int lastIndex = 3 * (count-1);
-			int lastS1 = ranges.get(lastIndex);
-			int lastS2 = ranges.get(lastIndex + 1);
-			int lastLen = ranges.get(lastIndex + 2);
-			if (start1 == lastS1 + lastLen && start2 == lastS2 + lastLen) {
-				// new range continues the previous one - just increase the length
-				ranges.set(lastIndex + 2, lastLen + length);
-				return;
-			}
-		}
-		ranges.add(start1, start2, length);
-		count++;
-	}
-	
-	public void clear() {
-		ranges.clear();
-		count = 0;
-	}
-
-	public int size() {
-		return count;
-	}
-
-	/**
-	 * find out line index in the target that matches specified origin line
-	 */
-	public int mapLineIndex(int ln) {
-		for (int i = 0; i < ranges.size(); i += 3) {
-			int s1 = ranges.get(i);
-			if (s1 > ln) {
-				return -1;
-			}
-			int l = ranges.get(i+2);
-			if (s1 + l > ln) {
-				int s2 = ranges.get(i + 1);
-				return s2 + (ln - s1);
-			}
-		}
-		return -1;
-	}
-	
-	/**
-	 * find out line index in origin that matches specified target line
-	 */
-	public int reverseMapLine(int targetLine) {
-		for (int i = 0; i < ranges.size(); i +=3) {
-			int ts = ranges.get(i + 1);
-			if (ts > targetLine) {
-				return -1;
-			}
-			int l = ranges.get(i + 2);
-			if (ts + l > targetLine) {
-				int os = ranges.get(i);
-				return os + (targetLine - ts);
-			}
-		}
-		return -1;
-	}
-	
-	public RangeSeq intersect(RangeSeq target) {
-		RangeSeq v = new RangeSeq();
-		for (int i = 0; i < ranges.size(); i += 3) {
-			int originLine = ranges.get(i);
-			int targetLine = ranges.get(i + 1);
-			int length = ranges.get(i + 2);
-			int startTargetLine = -1, startOriginLine = -1, c = 0;
-			for (int j = 0; j < length; j++) {
-				int lnInFinal = target.mapLineIndex(targetLine + j);
-				if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) {
-					// the line is not among "same" in ultimate origin
-					// or belongs to another/next "same" chunk 
-					if (startOriginLine == -1) {
-						continue;
-					}
-					v.add(startOriginLine, startTargetLine, c);
-					c = 0;
-					startOriginLine = startTargetLine = -1;
-					// fall-through to check if it's not complete miss but a next chunk
-				}
-				if (lnInFinal != -1) {
-					if (startOriginLine == -1) {
-						startOriginLine = originLine + j;
-						startTargetLine = lnInFinal;
-						c = 1;
-					} else {
-						// lnInFinal != startTargetLine + s is covered above
-						assert lnInFinal == startTargetLine + c;
-						c++;
-					}
-				}
-			}
-			if (startOriginLine != -1) {
-				assert c > 0;
-				v.add(startOriginLine, startTargetLine, c);
-			}
-		}
-		return v;
-	}
-	
-	// true when specified line in origin is equal to a line in target
-	public boolean includesOriginLine(int ln) {
-		return includes(ln, 0);
-	}
-	
-	// true when specified line in target is equal to a line in origin
-	public boolean includesTargetLine(int ln) {
-		return includes(ln, 1);
-	}
-
-	private boolean includes(int ln, int o) {
-		for (int i = 2; i < ranges.size(); o += 3, i+=3) {
-			int rangeStart = ranges.get(o);
-			if (rangeStart > ln) {
-				return false;
-			}
-			int rangeLen = ranges.get(i);
-			if (rangeStart + rangeLen > ln) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	public CharSequence dump() {
-		StringBuilder sb = new StringBuilder();
-		Formatter f = new Formatter(sb);
-		for (int i = 0; i < ranges.size(); i += 3) {
-			int s1 = ranges.get(i);
-			int s2 = ranges.get(i + 1);
-			int len = ranges.get(i + 2);
-			f.format("[%d..%d) == [%d..%d);  ", s1, s1 + len, s2, s2 + len);
-		}
-		return sb;
-	}
-	
-	@Override
-	public String toString() {
-		return String.format("RangeSeq[%d]:%s", count, dump());
-	}
-}
\ No newline at end of file
--- a/test/org/tmatesoft/hg/test/TestAuxUtilities.java	Fri Jul 12 20:14:24 2013 +0200
+++ b/test/org/tmatesoft/hg/test/TestAuxUtilities.java	Wed Jul 17 15:40:51 2013 +0200
@@ -32,9 +32,11 @@
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.ArrayHelper;
 import org.tmatesoft.hg.internal.ByteVector;
+import org.tmatesoft.hg.internal.IntSliceSeq;
+import org.tmatesoft.hg.internal.IntTuple;
 import org.tmatesoft.hg.internal.IntVector;
 import org.tmatesoft.hg.internal.PathScope;
-import org.tmatesoft.hg.internal.RangeSeq;
+import org.tmatesoft.hg.internal.RangePairSeq;
 import org.tmatesoft.hg.internal.RevisionDescendants;
 import org.tmatesoft.hg.repo.HgChangelog;
 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
@@ -511,8 +513,8 @@
 	}
 	
 	@Test
-	public void testRangeSequence() {
-		RangeSeq rs = new RangeSeq();
+	public void testRangePairSequence() {
+		RangePairSeq rs = new RangePairSeq();
 		rs.add(-1, 5, 3);
 		rs.add(-1, 10, 2);
 		rs.add(-1, 15, 3);
@@ -556,6 +558,38 @@
 		errorCollector.assertTrue(v.equalsTo(new byte[] { 10,9,8,7,6,5,4 }));
 	}
 	
+	@Test
+	public void testIntSliceSeq() {
+		IntSliceSeq s1 = new IntSliceSeq(3, 10, 10);
+		s1.add(1,2,3);
+		try {
+			s1.add(1,2);
+			errorCollector.fail("shall accept precise number of arguments");
+		} catch (IllegalArgumentException ex) {
+		}
+		try {
+			s1.add(1,2,3,4);
+			errorCollector.fail("shall accept precise number of arguments");
+		} catch (IllegalArgumentException ex) {
+		}
+		s1.add(21,22,23);
+		errorCollector.assertEquals(2, s1.size());
+		s1.add(7, 8, 9);
+		s1.set(1, 4, 5, 6);
+		IntTuple l = s1.last();
+		errorCollector.assertEquals(7, l.at(0));
+		errorCollector.assertEquals(8, l.at(1));
+		errorCollector.assertEquals(9, l.at(2));
+		int v = 1, slice = 0;
+		for (IntTuple t : s1) {
+			for (int i = 0; i < t.size(); i++) {
+				errorCollector.assertEquals(String.format("Slice %d, element %d", slice, i), v++, t.at(i));
+			}
+			slice++;
+		}
+		errorCollector.assertEquals(10, v);
+	}
+	
 	public static void main(String[] args) throws Throwable {
 		TestAuxUtilities t = new TestAuxUtilities();
 		t.testByteVector();