package util;
import java.util.AbstractList;
/**
* A list backed by (a slice of) an array.
*
* The $left and $right properties delimit the part of the array used by the
* slice. As is customary, $left is inclusive, $right is inclusive.
*
* The slice itself forms a circular array, whose index 0 is the element at
* array index $start.
*
* The $stride is a multiplicative factor applied to indices. A negative stride
* can be used to reverse the indices. A stride of n or -n can be used to access
* only one element in n.
*
* Regardless of stride, the size() method returns $left - $right.
*/
public class ArraySlice<T> extends AbstractList<T>
{
/**************************************************************************/
private final T[] array;
/**************************************************************************/
private int left, right, start, stride;
public T[] getArray () { return array; }
public int getStart () { return start; }
public int getLeft () { return left; }
public int getRight () { return right; }
public int getStride () { return stride; }
/**************************************************************************/
public ArraySlice(T[] array)
{
this(array, 0, array.length);
}
/****************************************************************************/
public ArraySlice(T[] array, int start, int end)
{
this(array, start, end, start, 1);
}
/****************************************************************************/
public ArraySlice(T[] array, int left, int right, int start, int stride)
{
this.array = array;
this.stride = stride;
this.start = start = absoluteIndex(start, array.length);
this.left = left = absoluteIndex(left, array.length);
this.right = right = absoluteIndex(right, array.length);
if (right < left) {
throw new NegativeArraySizeException(
"Trying to make a slice with left bound " + left
+ " and right bound" + right + ".");
}
if (left != right && (start < left || right <= start)) {
throw new IndexOutOfBoundsException(
"Start of slice is at " + start
+ ", while the bounds are [" + left + ", " + right + "[.");
}
}
/****************************************************************************/
private int absoluteIndex(int index, int size)
{
return (index < 0) ? (size + index) : (index);
}
/****************************************************************************/
@Override public int size()
{
return right - left;
}
/****************************************************************************/
private int index(int _index)
{
int index = _index;
index = stride * index;
index = absoluteIndex(index, size());
if (index < 0 || size() <= index)
{
throw new IndexOutOfBoundsException("index " + _index + " (stride: "
+ stride + ") in " + this.getClass().getName()
+ " instance of size " + size());
}
return (start + index) % array.length;
}
/****************************************************************************/
@Override public T get(int index)
{
return array[index(index)];
}
/****************************************************************************/
@Override public T set(int index, T element)
{
T out = get(index);
array[index(index)] = element;
return out;
}
/*****************************************************************************
* Shift all elements in the slice one index to the left. This is a
* constant-time operation that works by shifting $start.
*/
public void shiftLeft()
{
start = (size() > 1) ? index(-1) : start;
}
/*****************************************************************************
* Shift all elements in the slice one index to the right. This is a
* constant-time operation that works by shifting $start.
*/
public void shiftRight()
{
start = (size() > 1) ? index(1) : start;
}
/*****************************************************************************
* Shifts the left bound to the left, making the slice one element bigger.
*/
public void shiftLeftBound()
{
if (left - 1 < 0) {
throw new IndexOutOfBoundsException();
}
left = left - 1;
}
/*****************************************************************************
* Shifts the right bound to the right, making the slice one element bigger.
*/
public void shiftRightBound()
{
if (right > array.length) {
throw new IndexOutOfBoundsException();
}
right = right + 1;
}
/*****************************************************************************
* Tries to increase the slice's size, by shifting the right bound or the left
* bound. Throws an exception if the slice already occupies the whole array.
*/
public void growSlice()
{
if (right < array.length) {
shiftRightBound();
}
else if (left > 0) {
shiftLeftBound();
}
else {
throw new IllegalStateException(
"Trying to grow a slice that already occupies the whole array.");
}
}
/****************************************************************************/
public void setStride(int stride)
{
this.stride = stride;
}
}