package xxl.core.spatial.spatialBPlusTree;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import xxl.core.util.Pair;
/**
*
* @author achakeye
*
*/
public class AdaptiveZCurveMapper {
/*******************************************************
* Z Curve optimizations
*******************************************************/
/**
* This is an implementation of asymmetric z-curve.
*
* This method can be used for generation of adaptive sorting
*
* see D. Achakeev, B. Seeger P. Widmayer "Sort-based query-adaptive loading of R-trees" Technical Report, Philipps-University Marburg, 2012
* and CIKM 2012
*
* @param dimIndex sorted in ascending order g to the length of the query of dimensions e.g. [2,1,0] if the size of the query z dimension is smaller than x and y than
* @param prefixLength
* @param resolutions array of e.g. [6, 4 , 2] dimension z has resolution of 2 bits
* @return returns array with dimensions as a function e.g. for symmetric z-curve we return [0,1,0,1,0,1,0,1] if we have a 4 bits for each dimension
*/
public static int[] computeShuffleFunctionAspectRatio(int[] dimIndex, int[] prefixLength, int[] dimensionResolutions){
// compute the length of the key
int length = 0;
int[] resolutions = new int[dimensionResolutions.length];
System.arraycopy(dimensionResolutions, 0, resolutions, 0, resolutions.length);
for(int resolution : resolutions){
length+=resolution;
}
int[] mapFunction = new int[length];
// compute prefixes
Stack<Integer> suffix = new Stack<>();
int index=0;
for(int i = 0 ; i < prefixLength.length; i++){ // foreach prefix
int L = prefixLength[i];
for(int k = 0; k < L; k++){ // shuffle bits but only dimensions
for(int j = 0; j < dimIndex.length-i; j++){ // foreach sorted dimension value do
int dimension = dimIndex[j];
if(resolutions[dimension] > 0){
mapFunction[index]=dimension;
index++;
resolutions[dimension]--; // decrement available length
}
}
// put
}
suffix.push(dimIndex[prefixLength.length-i]);
}
suffix.push(dimIndex[0]); // last dimension
// compute suffix
while(!suffix.isEmpty()){
int dimension = suffix.pop();
int remainResolution = resolutions[dimension];
for(int i = 0; i < remainResolution; i++){
mapFunction[index] = dimension;
index++;
}
}
return mapFunction;
}
/**
* computes z value using a given z map function
* @param xyz
* @param mapFunction
* @param masks
* @return
*/
public static long computeZKey(int[] ddd, int[] mapFunction, int[] resolutions){
int[] res = new int[resolutions.length];
System.arraycopy(resolutions, 0, res, 0, res.length);
long key = 0L;
long maskPosition = 0L;
for(int i = 0, position = mapFunction.length-1; i < mapFunction.length; i++, position--){
int dimension = mapFunction[i];
//get
long dimensionVal = ddd[dimension];
if(res[dimension] > 0){
long mask = 1 << res[dimension]-1;
res[dimension]--;
dimensionVal &= mask; // get 0 or 1
if(dimensionVal > 0){ // set
maskPosition = 1L << position;
key |=maskPosition;
}
}
}
return key;
}
/**
* computes a ranges of a z-curve for a given rectangle.
* rectangle is given by low-left and upper-right point.
*
* @return list of ranges
*/
public static List<SpatialZQueryRange> computesRanges(int[] lowPoint, int[] highPoint,
final int[] mapFunction, final int[] resolutions, final int[] resolutionsAcc, final int lastDimensionFirstIndex, int hyperPlaneIndex){
// compute z values
List<SpatialZQueryRange> list = new LinkedList<>();
long low = computeZKey(lowPoint, mapFunction, resolutions);
long high = computeZKey(highPoint, mapFunction, resolutions);
if (low > high){
throw new RuntimeException("Low point > High point!");
}
//
if( low == high){
list.add(new SpatialZQueryRange(low, high));
return list;
}
//1. check if after (inclusive) index the lower point has following pattern 0000000...00000
//2. check if after (inclusive) index the upper point has following pattern 1111111...11111
// if not cut using hyperplane
// go through and define hyperplane cut on dimension where the firts 0 at low and 1 high
int hyperplaneIndex = hyperPlaneIndex;
long lowVal = 0L;
long highVal = 0L;
if (hyperplaneIndex <= lastDimensionFirstIndex){
list.add(new SpatialZQueryRange(low, high));
return list;
}
boolean equal = false;
for(int i = hyperPlaneIndex; (equal = (lowVal == highVal)) && i > lastDimensionFirstIndex; i--, hyperplaneIndex--){
// check if we need a cut
long mask = 1L << i;
lowVal = low & mask;
highVal = high & mask;
int index = mapFunction.length-1;
index = index-hyperplaneIndex;
int hyperPlane = mapFunction[index];
resolutionsAcc[hyperPlane] -=1;
}
if (hyperplaneIndex+1 <= lastDimensionFirstIndex || equal){
list.add(new SpatialZQueryRange(low, high));
return list;
}
long maskOnes = 1L << (hyperplaneIndex+2);//
maskOnes -=1L; //1111111111
//
boolean lowCheck = ((low & maskOnes) == 0L);
boolean highCheck = ((high & maskOnes) == maskOnes);
if(lowCheck && highCheck){
list.add(new SpatialZQueryRange(low, high));
return list;
}
int index = mapFunction.length-1;
index = index-hyperplaneIndex-1;
int hyperPlane = mapFunction[index];
int hyperPlaneValue = 1 << (resolutionsAcc[hyperPlane]);
Pair<QueryBox, QueryBox> split = cutQueryBox(lowPoint, highPoint, hyperPlane, hyperPlaneValue, resolutions);
QueryBox lowBox = split.getElement1();
QueryBox highBox = split.getElement2();
int[] lowAcc = new int[resolutionsAcc.length];
System.arraycopy(resolutionsAcc, 0, lowAcc, 0, resolutionsAcc.length);
List<SpatialZQueryRange> lowList = computesRanges(lowBox.getElement1(), lowBox.getElement2(), mapFunction, resolutions, lowAcc, lastDimensionFirstIndex, hyperplaneIndex);
int[] highAcc = new int[resolutionsAcc.length];
System.arraycopy(resolutionsAcc, 0, highAcc, 0, resolutionsAcc.length);
List<SpatialZQueryRange> highList = computesRanges(highBox.getElement1(), highBox.getElement2(), mapFunction, resolutions, highAcc, lastDimensionFirstIndex, hyperplaneIndex);
list.addAll(lowList);
list.addAll(highList);
lowList = null;
highList = null;
return list;
}
/**
* Cuts the hyperrectangles along a hyperplane
*
* Assumptions
*
* 1. box is reperesented by integer values low and high
* 2.
*
*
* we cut the box along the hyperplane set
*
* query ox is split in two boxes ->
* 1. Box
* low point is the old lower point
* high point is dimension values | H where H := 0000000111111...11111
* other dimensions have value of high point
* 2. Box
* low point is the old lower point dimension of hyperplane = (value & H) + 1
*
*
*/
private static Pair<QueryBox, QueryBox> cutQueryBox(int[] lowPoint, int[] highPoint,
int hyperPlaneDimension, int hyperplaneValue, final int[] resolutions){
int[] lowLow = new int[lowPoint.length];
int[] lowHigh = new int[lowPoint.length];
int[] highLow = new int[lowPoint.length];
int[] highHigh = new int[lowPoint.length];
System.arraycopy(lowPoint, 0, lowLow, 0, lowPoint.length);
System.arraycopy(highPoint, 0, lowHigh, 0, lowPoint.length);
System.arraycopy(lowPoint, 0, highLow, 0, lowPoint.length);
System.arraycopy(highPoint, 0, highHigh, 0, lowPoint.length);
int mask = hyperplaneValue-1;
lowHigh[hyperPlaneDimension] = lowLow[hyperPlaneDimension] | mask; //cut ->
highLow[hyperPlaneDimension] = lowHigh[hyperPlaneDimension] + 1;
return new Pair<>(new QueryBox(lowLow, lowHigh), new QueryBox(highLow, highHigh));
}
/**
*
* @param prefixLength
* @param dimensionResolutions
* @return
*/
public static int getLastDimensionPrefixIndex(int firstL, int bitsLastDim ){
int suffix = bitsLastDim - firstL;
return suffix-1;
}
/**
*
* @param dimensionResolutions
* @return
*/
public static int getIndexOfHighestBit(int[] dimensionResolutions){
int length = 0;
for(int resolution : dimensionResolutions){
length+=resolution;
}
return length-1;
}
/**
* type def class
* @author achakeye
*
*/
protected static class QueryBox extends Pair<int[], int[]>{
/**
*
* @param low
* @param high
*/
public QueryBox(int[] low, int[] high){
super(low, high);
}
}
/**
*
* @author achakeev
*
*/
public static class SpatialZQueryRange extends Pair<Long, Long>{
public SpatialZQueryRange(long min, long max){
super(min, max);
}
}
// /**
// * @param args
// */
// public static void main(String[] args) {
// // e.g. x,y,z
// // query Z > Y > X
// // resolutions
// // length L0 = 2; L1 = 3;
// // resolution x = 10 , y = 8 , z = 4 -> key = 22 bits
// // X= 0000 0000 00 , Y = 0000 0000, Z = 00 00
// // mapfunction [xyzxyz + xyxyxy + xxxxxyyy + zz]
// int x = 0;
// int y = 1;
// int z = 2;
// int[] dimensions = {0,1,2};
//
// int[] length = {2,3};
// int[] resolution = {10, 8, 4};
// int[] mapFunction = computeShuffleFunctionAspectRatio(
// dimensions, length, resolution);
// String arrayString = Arrays.toString(mapFunction);
// System.out.println(arrayString);
// // e.g. x,y,z
// // query Z > Y > X
// // resolutions
// // length L0 = 2; L1 = 4;
// // resolution x = 10 , y = 5 , z = 4 -> key = 19 bits
// // X= 0000 0000 00 , Y = 0000 0, Z = 00 00
// // mapfunction [xyzxyz + xyxyxyx + xxxx + zz]
// dimensions = new int[]{0,1,2};
// length = new int[]{2,4};
// resolution = new int[] {10, 5, 4};
// mapFunction = computeShuffleFunctionAspectRatio(
// dimensions, length, resolution);
// arrayString = Arrays.toString(mapFunction);
// System.out.println(arrayString);
// // function
// long key = computeZKey(new int[]{4,0}, new int[]{0,1,0,1,0,1}, new int[]{3,3});
// System.out.println(key);
// key = computeZKey(new int[]{3,3}, new int[]{0,1,0,1,0,1}, new int[]{3,3});
// System.out.println(key);
// }
}