long low = interleave(min), high = interleave(max); if (high < low) { throw new IllegalArgumentException(high + "<" + low); list.add(item); } else { int middle = findMiddle(min[largest], max[largest]); int temp = max[largest]; max[largest] = middle; addMortonRanges(list, min, max, len, level + 1); max[largest] = temp; temp = min[largest]; min[largest] = middle + 1; addMortonRanges(list, min, max, len, level + 1); min[largest] = temp;
/** * Executes a prepared query that was generated using generatePreparedQuery. * * @param prep the prepared statement * @param min the lower values * @param max the upper values * @return the result set */ public ResultSet getResult(PreparedStatement prep, int[] min, int[] max) throws SQLException { long[][] ranges = getMortonRanges(min, max); int len = ranges.length; Long[] from = new Long[len]; Long[] to = new Long[len]; for (int i = 0; i < len; i++) { from[i] = ranges[i][0]; to[i] = ranges[i][1]; } prep.setObject(1, from); prep.setObject(2, to); len = min.length; for (int i = 0, idx = 3; i < len; i++) { prep.setInt(idx++, min[i]); prep.setInt(idx++, max[i]); } return prep.executeQuery(); }
/** * Gets a list of ranges to be searched for a multi-dimensional range query * where min <= value <= max. In most cases, the ranges will be larger * than required in order to combine smaller ranges into one. Usually, about * double as many points will be included in the resulting range. * * @param min the minimum value * @param max the maximum value * @return the list of ranges (low, high pairs) */ private long[][] getMortonRanges(int[] min, int[] max) { int len = min.length; if (max.length != len) { throw new IllegalArgumentException(len + "=" + max.length); } for (int i = 0; i < len; i++) { if (min[i] > max[i]) { int temp = min[i]; min[i] = max[i]; max[i] = temp; } } int total = getSize(min, max, len); ArrayList<long[]> list = New.arrayList(); addMortonRanges(list, min, max, len, 0); combineEntries(list, total); return list.toArray(new long[0][]); }
/** * Convert the multi-dimensional value into a one-dimensional (scalar) * value. This is done by interleaving the bits of the values. Each values * must be between 0 (including) and the maximum value for the given number * of dimensions (getMaxValue, excluding). To normalize values to this * range, use the normalize function. * * @param values the multi-dimensional value * @return the scalar value */ public long interleave(int... values) { int dimensions = values.length; long max = getMaxValue(dimensions); int bitsPerValue = getBitsPerValue(dimensions); long x = 0; for (int i = 0; i < dimensions; i++) { long k = values[i]; if (k < 0 || k > max) { throw new IllegalArgumentException(0 + "<" + k + "<" + max); } for (int b = 0; b < bitsPerValue; b++) { x |= (k & (1L << b)) << (i + (dimensions - 1) * b); } } return x; }
/** * Gets a list of ranges to be searched for a multi-dimensional range query * where min <= value <= max. In most cases, the ranges will be larger * than required in order to combine smaller ranges into one. Usually, about * double as much points will be included in the resulting range. * * @param min the minimum value * @param max the maximum value * @return the list of ranges */ public long[][] getMortonRanges(int[] min, int[] max) { int len = min.length; if (max.length != len) { throw new Error("dimensions mismatch"); } for (int i = 0; i < len; i++) { if (min[i] > max[i]) { int temp = min[i]; min[i] = max[i]; max[i] = temp; } } int total = getSize(min, max, len); ArrayList list = new ArrayList(); addMortonRanges(list, min, max, len, 0); optimize(list, total); long[][] ranges = new long[list.size()][2]; list.toArray(ranges); return ranges; }
private static int findMiddle(int a, int b) { int diff = b - a - 1; if (diff == 0) { return a; } if (diff == 1) { return a + 1; } int scale = 0; while ((1 << scale) < diff) { scale++; } scale--; int m = roundUp(a + 2, 1 << scale) - 1; if (m <= a || m >= b) { throw new IllegalArgumentException(a + "<" + m + "<" + b); } return m; }
/** * Gets one of the original multi-dimensional values from a scalar value. * * @param dimensions the number of dimensions * @param scalar the scalar value * @param dim the dimension of the returned value (starting from 0) * @return the value */ public int deinterleave(int dimensions, long scalar, int dim) { int bitsPerValue = getBitsPerValue(dimensions); int value = 0; for (int i = 0; i < bitsPerValue; i++) { value |= (scalar >> (dim + (dimensions - 1) * i)) & (1L << i); } return value; }
/** * Normalize a value so that it is between the minimum and maximum for the * given number of dimensions. * * @param dimensions the number of dimensions * @param value the value (must be in the range min..max) * @param min the minimum value * @param max the maximum value (must be larger than min) * @return the normalized value in the range 0..getMaxValue(dimensions) */ public int normalize(int dimensions, double value, double min, double max) { if (value < min || value > max) { throw new IllegalArgumentException(min + "<" + value + "<" + max); } double x = (value - min) / (max - min); return (int) (x * getMaxValue(dimensions)); }
/** * Convert the multi-dimensional value into a one-dimensional (scalar) * value. This is done by interleaving the bits of the values. Each values * must be between 0 (including) and the maximum value for the given number * of dimensions (getMaxValue, excluding). To normalize values to this * range, use the normalize function. * * @param values the multi-dimensional value * @return the scalar value */ public long interleave(int... values) { int dimensions = values.length; long max = getMaxValue(dimensions); int bitsPerValue = getBitsPerValue(dimensions); long x = 0; for (int i = 0; i < dimensions; i++) { long k = values[i]; if (k < 0 || k > max) { throw new IllegalArgumentException(0 + "<" + k + "<" + max); } for (int b = 0; b < bitsPerValue; b++) { x |= (k & (1L << b)) << (i + (dimensions - 1) * b); } } return x; }
private static int findMiddle(int a, int b) { int diff = b - a - 1; if (diff == 0) { return a; } if (diff == 1) { return a + 1; } int scale = 0; while ((1 << scale) < diff) { scale++; } scale--; int m = roundUp(a + 2, 1 << scale) - 1; if (m <= a || m >= b) { throw new IllegalArgumentException(a + "<" + m + "<" + b); } return m; }
/** * Get the maximum value for the given dimension count. For two dimensions, * each value must contain at most 32 bit, for 3: 21 bit, 4: 16 bit, 5: 12 * bit, 6: 10 bit, 7: 9 bit, 8: 8 bit. * * @param dimensions the number of dimensions * @return the maximum value */ public int getMaxValue(int dimensions) { if (dimensions < 2 || dimensions > 32) { throw new IllegalArgumentException("" + dimensions); } int bitsPerValue = getBitsPerValue(dimensions); return (int) ((1L << bitsPerValue) - 1); }
/** * Normalize a value so that it is between the minimum and maximum for the * given number of dimensions. * * @param dimensions the number of dimensions * @param value the value (must be in the range min..max) * @param min the minimum value * @param max the maximum value (must be larger than min) * @return the normalized value in the range 0..getMaxValue(dimensions) */ public int normalize(int dimensions, double value, double min, double max) { if (value < min || value > max) { throw new IllegalArgumentException(min + "<" + value + "<" + max); } double x = (value - min) / (max - min); return (int) (x * getMaxValue(dimensions)); }
long low = interleave(min), high = interleave(max); if (high < low) { throw new Error("Stop"); list.add(item); } else { int middle = findMiddle(min[largest], max[largest]); int temp = max[largest]; max[largest] = middle; addMortonRanges(list, min, max, len, level + 1); max[largest] = temp; temp = min[largest]; min[largest] = middle + 1; addMortonRanges(list, min, max, len, level + 1); min[largest] = temp;
/** * Gets a list of ranges to be searched for a multi-dimensional range query * where min <= value <= max. In most cases, the ranges will be larger * than required in order to combine smaller ranges into one. Usually, about * double as many points will be included in the resulting range. * * @param min the minimum value * @param max the maximum value * @return the list of ranges (low, high pairs) */ private long[][] getMortonRanges(int[] min, int[] max) { int len = min.length; if (max.length != len) { throw new IllegalArgumentException(len + "=" + max.length); } for (int i = 0; i < len; i++) { if (min[i] > max[i]) { int temp = min[i]; min[i] = max[i]; max[i] = temp; } } int total = getSize(min, max, len); ArrayList<long[]> list = New.arrayList(); addMortonRanges(list, min, max, len, 0); combineEntries(list, total); long[][] ranges = new long[list.size()][2]; list.toArray(ranges); return ranges; }
/** * Convert the multi-dimensional value into a one-dimensional (scalar) * value. This is done by interleaving the bits of the values. Each values * must be between 0 (including) and the maximum value for the given number * of dimensions (getMaxValue, excluding). To normalize values to this * range, use the normalize function. * * @param values the multi-dimensional value * @return the scalar value */ public long interleave(int... values) { int dimensions = values.length; long max = getMaxValue(dimensions); int bitsPerValue = getBitsPerValue(dimensions); long x = 0; for (int i = 0; i < dimensions; i++) { long k = values[i]; if (k < 0 || k > max) { throw new IllegalArgumentException(0 + "<" + k + "<" + max); } for (int b = 0; b < bitsPerValue; b++) { x |= (k & (1L << b)) << (i + (dimensions - 1) * b); } } return x; }
/** * Executes a prepared query that was generated using generatePreparedQuery. * * @param prep the prepared statement * @param min the lower values * @param max the upper values * @return the result set */ public ResultSet getResult(PreparedStatement prep, int[] min, int[] max) throws SQLException { long[][] ranges = getMortonRanges(min, max); int len = ranges.length; Long[] from = new Long[len]; Long[] to = new Long[len]; for (int i = 0; i < len; i++) { from[i] = Long.valueOf(ranges[i][0]); to[i] = Long.valueOf(ranges[i][1]); } prep.setObject(1, from); prep.setObject(2, to); len = min.length; for (int i = 0, idx = 3; i < len; i++) { prep.setInt(idx++, min[i]); prep.setInt(idx++, max[i]); } return prep.executeQuery(); }
private int findMiddle(int a, int b) { int diff = b - a - 1; if (diff == 0) { return a; } if (diff == 1) { return a + 1; } int scale = 0; while ((1 << scale) < diff) { scale++; } scale--; int m = roundUp(a + 2, 1 << scale) - 1; if (m <= a || m >= b) { throw new Error("stop"); } return m; }
/** * Gets one of the original multi-dimensional values from a scalar value. * * @param dimensions the number of dimensions * @param scalar the scalar value * @param dim the dimension of the returned value (starting from 0) * @return the value */ public int deinterleave(int dimensions, long scalar, int dim) { int bitsPerValue = getBitsPerValue(dimensions); int value = 0; for (int i = 0; i < bitsPerValue; i++) { value |= (scalar >> (dim + (dimensions - 1) * i)) & (1L << i); } return value; }
/** * Normalize a value so that it is between the minimum and maximum for the * given number of dimensions. * * @param dimensions the number of dimensions * @param value the value (must be in the range min..max) * @param min the minimum value * @param max the maximum value (must be larger than min) * @return the normalized value in the range 0..getMaxValue(dimensions) */ public int normalize(int dimensions, double value, double min, double max) { if (value < min || value > max) { throw new IllegalArgumentException(min + "<" + value + "<" + max); } double x = (value - min) / (max - min); return (int) (x * getMaxValue(dimensions)); }
long low = interleave(min), high = interleave(max); if (high < low) { throw new IllegalArgumentException(high + "<" + low); list.add(item); } else { int middle = findMiddle(min[largest], max[largest]); int temp = max[largest]; max[largest] = middle; addMortonRanges(list, min, max, len, level + 1); max[largest] = temp; temp = min[largest]; min[largest] = middle + 1; addMortonRanges(list, min, max, len, level + 1); min[largest] = temp;