package org.opensha2.data;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex;
import static org.opensha2.data.Data.checkSize;
import com.google.common.primitives.Ints;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Utilities pertaining to the creation, calculation, and derivation of data
* indices.
*
* @author Peter Powers
*/
public final class Indexing {
private Indexing() {}
static final int INDICES_MAX_SIZE = 10000000;
private static final String INDICES_SIZE_ERROR = "Indices: size [%s] not premitted";
private static final String INDICES_INDEX_ERROR = "Indices: index [%s] not premitted";
/**
* Create an {@code int[]} of values ascending from {@code 0} to
* {@code 1-size}.
*
* @param size of output array
* @return an index array
* @throws IllegalArgumentException if {@code size} is not in the range
* {@code [1..10⁷]}
*/
public static int[] indices(int size) {
checkArgument(size > 0, INDICES_SIZE_ERROR, size);
checkArgument(size <= INDICES_MAX_SIZE, INDICES_SIZE_ERROR, size);
return indices(0, size - 1);
}
/**
* Create an {@code int[]} of values spanning {@code from} to {@code to},
* inclusive. Sequence will be descending if {@code from} is greater than
* {@code to}.
*
* @param from start value, inclusive
* @param to end value, inclusive
* @return an index array
* @throws IllegalArgumentException if {@code from < 0} or {@code to < 0}, or
* the computed size of the index array is {@code < 1}
*/
public static int[] indices(int from, int to) {
checkArgument(from >= 0, INDICES_INDEX_ERROR, from);
checkArgument(to >= 0, INDICES_INDEX_ERROR, to);
int size = Math.abs(from - to) + 1;
checkArgument(size <= INDICES_MAX_SIZE, INDICES_SIZE_ERROR, size);
int[] indices = new int[size];
int step = from < to ? 1 : -1;
for (int i = 0; i < size; i++) {
indices[i] = from + i * step;
}
return indices;
}
/**
* Create an index {@code List} of pointers to sorted {@code data}. Say you
* have a number of {@code List<Double>}s and want to iterate them according
* to the sort order of one of them. Supply this method with the desired
* {@code data} and use the returned indices in a custom iterator, leaving all
* original data in place.
*
* <p><b>Notes:</b><ul>
*
* <li>This method does not modify the supplied {@code data}.</li>
*
* <li>{@code NaN} is considered to be equal to itself and greater than all
* other double values (including Double.POSITIVE_INFINITY) per the behavior
* of {@link Double#compareTo(Double)}.</li> <ul>
*
* @param data for which to compute sort indices
* @param ascending sort order if {@code true}, descending if {@code false}
* @return a sorted index {@code List}
* @throws IllegalArgumentException if {@code data} is empty
*/
public static List<Integer> sortedIndices(List<Double> data, boolean ascending) {
checkSize(1, data);
List<Integer> indices = Ints.asList(indices(data.size()));
Collections.sort(indices, new IndexComparator(data, ascending));
return indices;
}
/*
* A comparator for ascending sorting of an index array based on the supplied
* double array of data.
*/
private static class IndexComparator implements Comparator<Integer> {
List<Double> data;
boolean ascending;
IndexComparator(List<Double> data, boolean ascending) {
this.data = data;
this.ascending = ascending;
}
@Override
public int compare(Integer i1, Integer i2) {
double d1 = data.get(ascending ? i1 : i2);
double d2 = data.get(ascending ? i2 : i1);
return Double.compare(d1, d2);
}
}
/**
* Return an index array corresponding to the 'set' bits of the supplied
* {@code BitSet}.
*
* @param bits to operate on
* @return the indices of 'set' bits
*/
public static int[] bitsToIndices(BitSet bits) {
int[] indices = new int[bits.cardinality()];
int index = 0;
for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) {
indices[index++] = i;
}
return indices;
}
/**
* Return a {@code BitSet} with {@code capacity} and with all bits at
* {@code indices} 'set'.
*
* @param indices to operate on
* @param capacity of returned {@code BitSet}
*/
public static BitSet indicesToBits(List<Integer> indices, int capacity) {
checkArgument(capacity > 0, "BitSet capacity [%s] must be > 0", capacity);
checkElementIndices(indices, capacity);
BitSet bits = new BitSet(capacity);
for (int index : indices) {
bits.set(index);
}
return bits;
}
/* Plural form of Guava's checkElementIndex(). */
private static void checkElementIndices(Collection<Integer> indices, int size) {
for (int index : indices) {
checkElementIndex(index, size);
}
}
/**
* Find the index of the minimum value in {@code data}. For equivalent minima,
* method returns the index of the first minimum encountered. If the supplied
* array is empty, method returns {@code -1}.
*
* @param data to evaluate
* @return the index of the minimum value or {@code -1} if the array is empty
*/
public static int minIndex(double... data) {
int index = -1;
double min = Double.POSITIVE_INFINITY;
for (int i = 1; i < data.length; i++) {
if (data[i] < min) {
index = i;
min = data[i];
}
}
return index;
}
/**
* Find the indices of the minimum value in {@code data}. For equivalent
* maxima, method returns the indices of the first minimum encountered. If the
* 1st dimension of the supplied array is empty or all arrays in the 2nd
* dimension are empty, method returns {@code [-1, -1]}.
*
* @param data to evaluate
* @return the indices of the minimum value or {@code [-1, -1]} for empty
* arrays
*/
public static int[] minIndex(double[][] data) {
int index0 = -1;
int index1 = -1;
double max = Double.POSITIVE_INFINITY;
for (int i = 0; i < data.length; i++) {
double[] data1 = data[i];
for (int j = 0; j < data1.length; j++) {
if (data1[j] < max) {
index0 = i;
index1 = j;
max = data1[j];
}
}
}
return new int[] { index0, index1 };
}
/**
* Find the indices of the minimum value in {@code data}. For equivalent
* minima, method returns the indices of the first minimum encountered. If the
* 1st dimension of the supplied array is empty or all arrays in the 2nd or
* 3rd dimensions are empty, method returns {@code [-1, -1, -1]}.
*
* @param data to evaluate
* @return the indices of the minimum value or {@code [-1, -1, -1]} for empty
* arrays
*/
public static int[] minIndex(double[][][] data) {
int index0 = -1;
int index1 = -1;
int index2 = -1;
double max = Double.POSITIVE_INFINITY;
for (int i = 0; i < data.length; i++) {
double[][] data1 = data[i];
for (int j = 0; j < data1.length; j++) {
double[] data2 = data1[j];
for (int k = 0; k < data2.length; k++) {
if (data2[k] < max) {
index0 = i;
index1 = j;
index2 = k;
max = data2[k];
}
}
}
}
return new int[] { index0, index1, index2 };
}
/**
* Find the index of the maximum value in {@code data}. For equivalent maxima,
* method returns the index of the first maximum encountered. If the supplied
* array is empty, method returns {@code -1}.
*
* @param data to evaluate
* @return the index of the maximum value or -1 if the array is empty
*/
public static int maxIndex(double... data) {
int index = -1;
double max = Double.NEGATIVE_INFINITY;
for (int i = 1; i < data.length; i++) {
if (data[i] > max) {
index = i;
max = data[i];
}
}
return index;
}
/**
* Find the indices of the maximum value in {@code data}. For equivalent
* maxima, method returns the indices of the first maximum encountered. If the
* 1st dimension of the supplied array is empty or all arrays in the 2nd
* dimension are empty, method returns {@code [-1, -1]}.
*
* @param data to evaluate
* @return the indices of the maximum value or {@code [-1, -1]} for empty
* arrays
*/
public static int[] maxIndex(double[][] data) {
int index0 = -1;
int index1 = -1;
double max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < data.length; i++) {
double[] data1 = data[i];
for (int j = 0; j < data1.length; j++) {
if (data1[j] > max) {
index0 = i;
index1 = j;
max = data1[j];
}
}
}
return new int[] { index0, index1 };
}
/**
* Find the indices of the maximum value in {@code data}. For equivalent
* maxima, method returns the indices of the first maximum encountered. If the
* 1st dimension of the supplied array is empty or all arrays in the 2nd or
* 3rd dimensions are empty, method returns {@code [-1, -1, -1]}.
*
* @param data to evaluate
* @return the indices of the maximum value or {@code [-1, -1, -1]} for empty
* arrays
*/
public static int[] maxIndex(double[][][] data) {
int index0 = -1;
int index1 = -1;
int index2 = -1;
double max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < data.length; i++) {
double[][] data1 = data[i];
for (int j = 0; j < data1.length; j++) {
double[] data2 = data1[j];
for (int k = 0; k < data2.length; k++) {
if (data2[k] > max) {
index0 = i;
index1 = j;
index2 = k;
max = data2[k];
}
}
}
}
return new int[] { index0, index1, index2 };
}
}