package mil.nga.giat.geowave.core.index;
import java.util.List;
import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition;
import mil.nga.giat.geowave.core.index.dimension.bin.BinRange;
import mil.nga.giat.geowave.core.index.sfc.data.BasicNumericDataset;
import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData;
import mil.nga.giat.geowave.core.index.sfc.data.NumericRange;
public class IndexUtils
{
public static MultiDimensionalNumericData getFullBounds(
final NumericIndexStrategy indexStrategy ) {
return getFullBounds(indexStrategy.getOrderedDimensionDefinitions());
}
/**
* Constraints that are empty indicate full table scan. A full table scan
* occurs if ANY one dimension is unbounded.
*
* @param constraints
* @return true if any one dimension is unbounded
*/
public static final boolean isFullTableScan(
final List<MultiDimensionalNumericData> constraints ) {
for (final MultiDimensionalNumericData constraint : constraints) {
if (constraint.isEmpty()) {
return false;
}
}
return constraints.isEmpty();
}
public static MultiDimensionalNumericData getFullBounds(
final NumericDimensionDefinition[] dimensionDefinitions ) {
final NumericRange[] boundsPerDimension = new NumericRange[dimensionDefinitions.length];
for (int d = 0; d < dimensionDefinitions.length; d++) {
boundsPerDimension[d] = dimensionDefinitions[d].getBounds();
}
return new BasicNumericDataset(
boundsPerDimension);
}
public static final double getDimensionalBitsUsed(
final NumericIndexStrategy indexStrategy,
final double[] dataRangePerDimension ) {
double result = Long.MAX_VALUE;
if (dataRangePerDimension.length == 0) {
return 0;
}
final double cellRangePerDimension[] = indexStrategy.getHighestPrecisionIdRangePerDimension();
final double inflatedRangePerDimension[] = inflateRange(
cellRangePerDimension,
dataRangePerDimension);
final double bitsPerDimension[] = getBitsPerDimension(
indexStrategy,
cellRangePerDimension);
final BinRange[][] binsPerDimension = getBinsPerDimension(
indexStrategy,
inflatedRangePerDimension);
final double[][] bitsFromTheRightPerDimension = getBitsFromTheRightPerDimension(
binsPerDimension,
cellRangePerDimension);
// This ALWAYS chooses the index who dimension
// cells cover the widest range thus fewest cells. In temporal, YEAR is
// always chosen.
// However, this is not necessarily bad. A smaller bin size may result
// in more bins searched.
// When searching across multiple bins associated with a dimension, The
// first and last bin are
// partial searches. The inner bins are 'full' scans over the bin.
// Thus, smaller bin sizes could result more in more rows scanned.
// On the flip, fewer larger less-granular bins can also have the same
// result.
// Bottom line: this is not straight forward
// Example: YEAR
// d[ 3600000.0]
// cellRangePerDimension[30157.470702171326]
// inflatedRangePerDimension[3618896.484260559]
// bitsFromTheRightPerDimension[6.906890595608519]]
// Example: DAY
// cellRangePerDimension[ 2554.3212881088257]
// inflatedRangePerDimension[ 3601593.016233444]
// bitsFromTheRightPerDimension[ 10.461479447286157]]
for (final double[] binnedBitsPerFromTheRightDimension : bitsFromTheRightPerDimension) {
for (int d = 0; d < binnedBitsPerFromTheRightDimension.length; d++) {
final double totalBitsUsed = (bitsPerDimension[d] - binnedBitsPerFromTheRightDimension[d]);
if (totalBitsUsed < 0) {
return 0;
}
result = Math.min(
totalBitsUsed,
result);
}
}
// The least constraining dimension uses the least amount of bits of
// fixed bits from the left.
// For example, half of the world latitude is 1 bit, 1/4 of the world is
// 2 bits etc.
// Use the least constraining dimension, but multiply by the
// # of dimensions.
return result * cellRangePerDimension.length;
}
public static double[] inflateRange(
final double[] cellRangePerDimension,
final double[] dataRangePerDimension ) {
final double[] result = new double[cellRangePerDimension.length];
for (int d = 0; d < result.length; d++) {
result[d] = Math.ceil(dataRangePerDimension[d] / cellRangePerDimension[d]) * cellRangePerDimension[d];
}
return result;
}
public static double[][] getBitsFromTheRightPerDimension(
final BinRange[][] binsPerDimension,
final double[] cellRangePerDimension ) {
int numBinnedQueries = 1;
for (int d = 0; d < binsPerDimension.length; d++) {
numBinnedQueries *= binsPerDimension[d].length;
}
// now we need to combine all permutations of bin ranges into
// BinnedQuery objects
final double[][] binnedQueries = new double[numBinnedQueries][];
for (int d = 0; d < binsPerDimension.length; d++) {
for (int b = 0; b < binsPerDimension[d].length; b++) {
for (int i = b; i < numBinnedQueries; i += binsPerDimension[d].length) {
if (binnedQueries[i] == null) {
binnedQueries[i] = new double[binsPerDimension.length];
}
if ((binsPerDimension[d][b].getNormalizedMax() - binsPerDimension[d][b].getNormalizedMin()) <= 0.000000001) {
binnedQueries[i][d] = 0;
}
else {
binnedQueries[i][d] = log2(Math
.ceil((binsPerDimension[d][b].getNormalizedMax() - binsPerDimension[d][b]
.getNormalizedMin()) / cellRangePerDimension[d]));
}
}
}
}
return binnedQueries;
}
public static int getBitPositionFromSubsamplingArray(
final NumericIndexStrategy indexStrategy,
final double[] maxResolutionSubsamplingPerDimension ) {
return (int) Math.round(getDimensionalBitsUsed(
indexStrategy,
maxResolutionSubsamplingPerDimension) + (8 * indexStrategy.getByteOffsetFromDimensionalIndex()));
}
public static byte[] getNextRowForSkip(
final byte[] row,
final int bitPosition ) {
final int cardinality = bitPosition + 1;
final byte[] rowCopy = new byte[(int) Math.ceil(cardinality / 8.0)];
System.arraycopy(
row,
0,
rowCopy,
0,
rowCopy.length);
// number of bits not used in the last byte
int remainder = (8 - (cardinality % 8));
if (remainder == 8) {
remainder = 0;
}
final int numIncrements = (int) Math.pow(
2,
remainder);
if (remainder > 0) {
for (int i = 0; i < remainder; i++) {
rowCopy[rowCopy.length - 1] |= (1 << (i));
}
}
for (int i = 0; i < numIncrements; i++) {
if (!ByteArrayUtils.increment(rowCopy)) {
return null;
}
}
return rowCopy;
}
private static final double[] getBitsPerDimension(
final NumericIndexStrategy indexStrategy,
final double[] rangePerDimension ) {
final NumericDimensionDefinition dim[] = indexStrategy.getOrderedDimensionDefinitions();
final double result[] = new double[rangePerDimension.length];
for (int d = 0; d < rangePerDimension.length; d++) {
result[d] += Math.ceil(log2((dim[d].getRange() / rangePerDimension[d])));
}
return result;
}
private static final BinRange[][] getBinsPerDimension(
final NumericIndexStrategy indexStrategy,
final double[] rangePerDimension ) {
final NumericDimensionDefinition dim[] = indexStrategy.getOrderedDimensionDefinitions();
final BinRange[][] result = new BinRange[rangePerDimension.length][];
for (int d = 0; d < rangePerDimension.length; d++) {
final BinRange[] ranges = dim[d].getNormalizedRanges(new NumericRange(
0,
rangePerDimension[d]));
result[d] = ranges;
}
return result;
}
private static double log2(
final double v ) {
return Math.log(v) / Math.log(2);
}
}