package DPJRuntime;
/**
* <p>The {@code Partition} class represents an array of {@link
* ArraySlice} objects that partition another {@code ArraySlice}
* (called the <i>root array</i>). Each {@code ArraySlice} in the
* partition is a contiguous subsection of the root array, and all are
* mutually disjoint. The {@code ArraySlice}s in the partition are
* called <i>segments</i> of the root array.
*
* <p>For example, if the root array has index space {@code [0,10]},
* then a partition might have segments with index spaces {@code
* [0,5]} and {@code [6,10]}.
*
* @author Rob Bocchino
*
* @param <T> The type of an element of an array in the partition
* @param <R> The region of a cell of an array in the partition
*/
public class Partition<type T,region R> {
/**
* The array being partitioned
*/
private final ArraySlice<T,R> A;
/**
* The segments of the partition
*/
private arrayclass Segs<region R> {
ArraySlice<T,R:[index]> in R:[index];
}
private final Segs<this> segs;
/**
* Number of segments in the partition
*/
public final int length;
/**
* Stride of the segments, for a strided partition.
*/
private final int stride;
/**
* Partitions an array {@code A} into two segments at index {@code
* idx}. If {@code A} has {@code n} elements, then the first
* segment consists of elements {@code 0} through {@code idx-1},
* and the second segment consists of elements {@code idx} through
* {@code n-1}.
*
* <p>Throws {@code ArrayIndexOutOfBoundsException} if
* {@code idx} is not in {@code [0,n-1]}.
*
* @param A The array to partition
* @param idx The partition index
*/
public Partition(ArraySlice<T,R> A, final int idx)
writes this:[?] {
this.A = A;
this.length = 2;
this.stride = 0;
segs = (Segs<this>) ((Object) new Object[length]);
segs[0] = (ArraySlice<T,this:[0]>)
A.subslice(0, idx);
segs[1] = (ArraySlice<T,this:[1]>)
A.subslice(idx, A.length - idx);
}
/**
* Partitions an array {@code A} into two segments at {@code idx},
* optionally excluding the element at {@code idx}. If {@code A}
* has {@code n} elements, then the first segment is always {@code
* [0,idx-1]}. The second segment is either {@code [idx,n-1]} (if
* {@code exclude} is false) or {@code [idx+1,n-1]} (if {@code
* exclude} is true).
*
* <p>Throws {@code ArrayIndexOutOfBoundsException} if
* {@code idx} is not in {@code [0,n-1]}.
*
* @param A The array to partition
* @param idx The partition index
* @param exclude Whether to exclude the element at
* {@code idx} from the segments
*/
public Partition(ArraySlice<T,R> A,
final int idx, boolean exclude)
writes this:[?] {
this.A = A;
this.length = 2;
this.stride = 0;
this.segs = (Segs<this>) ((Object) new Object[length]);
segs[0] = (ArraySlice<T,this:[0]>) A.subslice(0, idx);
if (exclude) {
segs[1] = (ArraySlice<T,this:[1]>)
A.subslice(idx + 1, A.length - idx - 1);
} else {
segs[1] = (ArraySlice<T,this:[1]>)
A.subslice(idx, A.length - idx);
}
}
/**
* Private constructor. {@code strided} is a dead arg here: we
* use it to disambiguate this constructor from the other one that
* takes an array and a stride.
*/
private Partition(ArraySlice<T,R> A,
int stride, double strided)
pure
{
this.A = A;
this.stride = stride;
this.length = (A.length / stride) +
((A.length % stride == 0) ? 0 : 1);
this.segs = null;
}
/**
* Creates a partition using stride {@code stride}. For example,
* partitioning a 10-element array with stride 2 creates a
* partition with 5 segments, each of length 2.
*
* @param <T> The type of the array to partition
* @param <R> The region of the array to partition
* @param A The array to partition
* @param stride The stride at which to partition
* @return A partition of {@code A} with stride {@code stride}
*/
public static <type T,region R>Partition<T,R>
stridedPartition(ArraySlice<T,R> A, int stride) pure {
return new Partition<T,R>(A, stride, 0.0);
}
/**
* Partitions an array {@code A} into
* {@code idxs.length+1} segments using the indices in
* {@code idxs} as the split points. If {@code A} has
* index space {@code [0,n-1]}, then the segments are
* {@code [0,idxs[0]-1]}, {@code [idxs[0],idxs[1]-1]},
* ..., {@code [idxs[idxs.length-1]-1,n-1]}.
*
* Throws {@code ArrayOutOfBoundsException} if the indices
* are not monotonically increasing, or if any index is out of
* bounds for {@code A}.
*
* @param <RI> The region of array {@code idxs}
* @param A The array to partition
* @param idxs The split points
* @return A partition of {@code A} using {@code idxs} as the
* split points.
*/
public <region RI>Partition(ArraySlice<T,R> A,
int[]<RI> idxs)
reads RI writes this:[?]
{
this.A = A;
this.length = idxs.length+1;
this.segs = (Segs<this>) ((Object) new Object[length]);
this.stride = 0;
if (length == 1)
segs[0] = (ArraySlice<T,this:[0]>) A;
else {
int i = 0, len = 0;
segs[0] = (ArraySlice<T,this:[0]>) A.subslice(0, idxs[0]);
for (i = 1; i < idxs.length; ++i) {
len = idxs[i] - idxs[i-1];
if (len < 0)
throw new ArrayIndexOutOfBoundsException();
final int j = i;
segs[j] = (ArraySlice<T,this:[j]>)
A.subslice(idxs[j-1], len);
}
i = idxs[idxs.length - 1];
len = A.length - i;
final int length = idxs.length;
segs[length] = (ArraySlice<T,this:[length]>)
A.subslice(i, len);
}
}
/**
* Returns segment {@code idx} of the partition.
*
* <p> Throws {@code ArrayIndexOutOfBoundsException} if {@code
* idx} is not a valid segment index.
*
* @param idx Index of the segment to get
* @return Segment {@code idx} of the partition
*/
public ArraySlice<T,this:[idx]:*> get(final int idx)
reads this:[idx]
{
if (idx < 0 || idx > length-1) {
throw new ArrayIndexOutOfBoundsException();
}
if (segs != null)
return segs[idx];
else {
int start = idx * stride;
int segLength = (start + stride > A.length) ?
(A.length - start) : stride;
return (ArraySlice<T,this:[idx]:*>)
A.subslice(start, segLength);
}
}
}