tikhomirov@520: /*
tikhomirov@520:  * Copyright (c) 2012 TMate Software Ltd
tikhomirov@520:  *  
tikhomirov@520:  * This program is free software; you can redistribute it and/or modify
tikhomirov@520:  * it under the terms of the GNU General Public License as published by
tikhomirov@520:  * the Free Software Foundation; version 2 of the License.
tikhomirov@520:  *
tikhomirov@520:  * This program is distributed in the hope that it will be useful,
tikhomirov@520:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@520:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@520:  * GNU General Public License for more details.
tikhomirov@520:  *
tikhomirov@520:  * For information on how to redistribute this software under
tikhomirov@520:  * the terms of a license other than GNU General Public License
tikhomirov@520:  * contact TMate Software at support@hg4j.com
tikhomirov@520:  */
tikhomirov@520: package org.tmatesoft.hg.internal;
tikhomirov@520: 
tikhomirov@520: import java.util.Arrays;
tikhomirov@520: import java.util.NoSuchElementException;
tikhomirov@520: 
tikhomirov@520: /**
tikhomirov@520:  * Helper to break given range [start..end] (inclusive bounds) to series of ranges,
tikhomirov@520:  * all but last are of batchSize length, and the last one is at most of batchSize+batchSizeTolerance length.
tikhomirov@520:  * 
tikhomirov@520:  * Range is [{@link #start()rangeStart}..{@link #end() rangeEnd}], where rangeStart is less or equal to rangeEnd.
tikhomirov@520:  * 
tikhomirov@520:  * When reverse range iteration is requested, original range is iterated from end to start, but the subranges 
tikhomirov@520:  * boundaries are in natural order. i.e. for 0..100, 10 first subrange would be [91..100], not [100..91]. This 
tikhomirov@520:  * helps clients of this class to get [start()..end()] in natural order regardless of iteration direction.
tikhomirov@520:  * 
tikhomirov@520:  * Note, this class (and its treatment of inclusive boundaries) is designed solely for use with methods that navigate
tikhomirov@520:  * revlogs and take (start,end) pair of inclusive range.
tikhomirov@520:  *  
tikhomirov@520:  * @author Artem Tikhomirov
tikhomirov@520:  * @author TMate Software Ltd.
tikhomirov@520:  */
tikhomirov@520: public class BatchRangeHelper {
tikhomirov@520: 
tikhomirov@520: 	private final int rangeCount;
tikhomirov@520: 	private final int rangeDelta;
tikhomirov@520: 	private final int nextValueDelta;
tikhomirov@520: 	private final int firstBoundary, lastBoundary;
tikhomirov@520: 	private int rangeIndex, rangeValue, rangeStart, rangeEnd;
tikhomirov@520: 
tikhomirov@520: 	public BatchRangeHelper(int start, int end, int batchSize, boolean reverse) {
tikhomirov@520: 		this(start, end, batchSize, batchSize/5, reverse); 
tikhomirov@520: 	}
tikhomirov@520: 
tikhomirov@520: 	public BatchRangeHelper(int start, int end, int batchSize, int batchSizeTolerance, boolean reverse) {
tikhomirov@520: 		assert end >= start;
tikhomirov@520: 		assert start >= 0;
tikhomirov@520: 		assert batchSize > 0;
tikhomirov@520: 		assert batchSizeTolerance >= 0;
tikhomirov@520: 		final int totalElements = end-start+1;
tikhomirov@520: 		int batchRangeCount = totalElements / batchSize;
tikhomirov@520: 		// batchRangeCount == 0, totalElements > 0 => need at least 1 range
tikhomirov@520: 		if (batchRangeCount == 0 || batchRangeCount*batchSize+batchSizeTolerance < totalElements) {
tikhomirov@520: 			batchRangeCount++;
tikhomirov@520: 		}
tikhomirov@520: 		rangeCount = batchRangeCount;
tikhomirov@520: 		rangeDelta = batchSize-1; // ranges are inclusive, and always grow naturally.
tikhomirov@520: 		nextValueDelta = reverse ? -batchSize : batchSize;
tikhomirov@520: 		firstBoundary = reverse ? end-rangeDelta : start;
tikhomirov@520: 		lastBoundary = reverse ? start : end;
tikhomirov@520: 		reset();
tikhomirov@520: 	}
tikhomirov@520: 
tikhomirov@520: 	public boolean hasNext() {
tikhomirov@520: 		return rangeIndex < rangeCount;
tikhomirov@520: 	}
tikhomirov@520: 	
tikhomirov@520: 	public void next() {
tikhomirov@520: 		if (!hasNext()) {
tikhomirov@520: 			throw new NoSuchElementException();
tikhomirov@520: 		}
tikhomirov@520: 		rangeStart = rangeValue;
tikhomirov@520: 		rangeEnd = rangeValue + rangeDelta;
tikhomirov@520: 		rangeValue += nextValueDelta;
tikhomirov@520: 		if (++rangeIndex >= rangeCount) {
tikhomirov@520: 			if (nextValueDelta < 0) {
tikhomirov@520: 				// reverse iteration, lastBoundary represents start
tikhomirov@520: 				rangeStart = lastBoundary;
tikhomirov@520: 			} else {
tikhomirov@520: 				// lastBoundary represents end
tikhomirov@520: 				rangeEnd = lastBoundary;
tikhomirov@520: 			}
tikhomirov@520: 		}
tikhomirov@520: 	}
tikhomirov@520: 	
tikhomirov@520: 	public int start() {
tikhomirov@520: 		return rangeStart;
tikhomirov@520: 	}
tikhomirov@520: 	
tikhomirov@520: 	public int end() {
tikhomirov@520: 		return rangeEnd;
tikhomirov@520: 	}
tikhomirov@520: 	
tikhomirov@520: 	public BatchRangeHelper reset() {
tikhomirov@520: 		rangeValue = firstBoundary;
tikhomirov@520: 		rangeIndex = 0;
tikhomirov@520: 		return this;
tikhomirov@520: 	}
tikhomirov@520: 
tikhomirov@520: 	public int[] toArray() {
tikhomirov@520: 		int[] rv = new int[rangeCount*2];
tikhomirov@520: 		reset();
tikhomirov@520: 		int i = 0;
tikhomirov@520: 		while (hasNext()) {
tikhomirov@520: 			next();
tikhomirov@520: 			rv[i++] = start();
tikhomirov@520: 			rv[i++] = end();
tikhomirov@520: 		}
tikhomirov@520: 		reset();
tikhomirov@520: 		return rv;
tikhomirov@520: 	}
tikhomirov@520: 
tikhomirov@520: 	public static void main(String[] args) {
tikhomirov@520: 		System.out.println("With remainder within tolerance");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 102, 10, 4, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 102, 10, 4, true).toArray()));
tikhomirov@520: 		System.out.println("With remainder out of tolerance");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 102, 10, 2, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 102, 10, 2, true).toArray()));
tikhomirov@520: 		System.out.println("Range smaller than batch");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 9, 10, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 9, 10, true).toArray()));
tikhomirov@520: 		System.out.println("Range smaller than batch and smaller than tolerance");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 9, 10, 20, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 9, 10, 20, true).toArray()));
tikhomirov@520: 		System.out.println("Zero tolerance");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 100, 10, 0, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(0, 100, 10, 0, true).toArray()));
tikhomirov@520: 		System.out.println("Right to boundary");
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 100, 10, false).toArray()));
tikhomirov@520: 		System.out.println(Arrays.toString(new BatchRangeHelper(1, 100, 10, true).toArray()));
tikhomirov@520: 	}
tikhomirov@520: }