blob: c6de1a5d8c29a33512e8eb6e0c8017844f09d5fc [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.datasketches.kll;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE_UNALIGNED;
import static org.apache.datasketches.common.ByteArrayUtil.putDoubleLE;
import static org.apache.datasketches.kll.KllPreambleUtil.DATA_START_ADR;
import static org.apache.datasketches.kll.KllPreambleUtil.DATA_START_ADR_SINGLE_ITEM;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_EMPTY;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_FULL;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.COMPACT_SINGLE;
import static org.apache.datasketches.kll.KllSketch.SketchStructure.UPDATABLE;
import static org.apache.datasketches.kll.KllSketch.SketchType.KLL_DOUBLES_SKETCH;
import java.lang.foreign.MemorySegment;
import java.util.Arrays;
import java.util.Objects;
import org.apache.datasketches.common.MemorySegmentRequest;
import org.apache.datasketches.common.SketchesArgumentException;
/**
* This class implements an on-heap doubles KllSketch.
*
* <p>Please refer to the documentation in the package-info:<br>
* {@link org.apache.datasketches.kll}</p>
*
* @author Lee Rhodes, Kevin Lang
*/
final class KllHeapDoublesSketch extends KllDoublesSketch {
private final int k; // configured size of K.
private final int m; // configured size of M.
private long n; // number of items input into this sketch.
private int minK; // dynamic minK for error estimation after merging with different k.
private boolean isLevelZeroSorted;
private double minDoubleItem;
private double maxDoubleItem;
private double[] doubleItems;
/**
* New instance heap constructor with a given parameters <em>k</em> and <em>m</em>.
*
* @param k parameter that controls size of the sketch and accuracy of estimates.
* <em>k</em> can be between <em>m</em> and 65535, inclusive.
* @param m parameter controls the minimum level width in items. It can be 2, 4, 6 or 8.
* The DEFAULT_M, which is 8 is recommended. Other sizes of <em>m</em> should be considered
* experimental as they have not been as well characterized.
*/
KllHeapDoublesSketch(final int k, final int m) {
super(UPDATABLE);
KllHelper.checkM(m);
KllHelper.checkK(k, m);
levelsArr = new int[] {k, k};
readOnly = false;
this.k = k;
this.m = m;
n = 0;
minK = k;
isLevelZeroSorted = false;
minDoubleItem = Double.NaN;
maxDoubleItem = Double.NaN;
doubleItems = new double[k];
}
/**
* Used for creating a temporary sketch for use with weighted updates.
*/
KllHeapDoublesSketch(final int k, final int m, final double item, final long weight) {
super(UPDATABLE);
KllHelper.checkM(m);
KllHelper.checkK(k, m);
levelsArr = KllHelper.createLevelsArray(weight);
readOnly = false;
this.k = k;
this.m = m;
n = weight;
minK = k;
isLevelZeroSorted = false;
minDoubleItem = item;
maxDoubleItem = item;
doubleItems = KllDoublesHelper.createItemsArray(item, weight);
}
/**
* Heapify constructor.
* @param srcSeg MemorySegment object that contains data serialized by this sketch.
* @param segValidate the MemoryValidate object
*/
private KllHeapDoublesSketch(
final MemorySegment srcSeg,
final KllMemorySegmentValidate segValidate) {
super(UPDATABLE);
final SketchStructure segStructure = segValidate.sketchStructure;
k = segValidate.k;
m = segValidate.m;
n = segValidate.n;
minK = segValidate.minK;
levelsArr = segValidate.levelsArr; //normalized to full
isLevelZeroSorted = segValidate.level0SortedFlag;
if (segStructure == COMPACT_EMPTY) {
minDoubleItem = Double.NaN;
maxDoubleItem = Double.NaN;
doubleItems = new double[k];
}
else if (segStructure == COMPACT_SINGLE) {
final double item = srcSeg.get(JAVA_DOUBLE_UNALIGNED, DATA_START_ADR_SINGLE_ITEM);
minDoubleItem = maxDoubleItem = item;
doubleItems = new double[k];
doubleItems[k - 1] = item;
}
else if (segStructure == COMPACT_FULL) {
int offsetBytes = DATA_START_ADR;
offsetBytes += (levelsArr.length - 1) * Integer.BYTES; //shortened levelsArr
minDoubleItem = srcSeg.get(JAVA_DOUBLE_UNALIGNED, offsetBytes);
offsetBytes += Double.BYTES;
maxDoubleItem = srcSeg.get(JAVA_DOUBLE_UNALIGNED, offsetBytes);
offsetBytes += Double.BYTES;
final int capacityItems = levelsArr[getNumLevels()];
final int freeSpace = levelsArr[0];
final int retainedItems = capacityItems - freeSpace;
doubleItems = new double[capacityItems];
MemorySegment.copy(srcSeg, JAVA_DOUBLE_UNALIGNED, offsetBytes, doubleItems, freeSpace, retainedItems);
}
else { //(segStructure == UPDATABLE)
int offsetBytes = DATA_START_ADR;
offsetBytes += levelsArr.length * Integer.BYTES; //full levelsArr
minDoubleItem = srcSeg.get(JAVA_DOUBLE_UNALIGNED, offsetBytes);
offsetBytes += Double.BYTES;
maxDoubleItem = srcSeg.get(JAVA_DOUBLE_UNALIGNED, offsetBytes);
offsetBytes += Double.BYTES;
final int capacityItems = levelsArr[getNumLevels()];
doubleItems = new double[capacityItems];
MemorySegment.copy(srcSeg, JAVA_DOUBLE_UNALIGNED, offsetBytes, doubleItems, 0, capacityItems);
}
}
static KllHeapDoublesSketch heapifyImpl(final MemorySegment srcSeg) {
Objects.requireNonNull(srcSeg, "Parameter 'srcSeg' must not be null");
final KllMemorySegmentValidate segVal = new KllMemorySegmentValidate(srcSeg, KLL_DOUBLES_SKETCH);
return new KllHeapDoublesSketch(srcSeg, segVal);
}
//End of constructors
@Override
String getItemAsString(final int index) {
if (isEmpty()) { return "NaN"; }
return Double.toString(doubleItems[index]);
}
@Override
public int getK() { return k; }
//MinMax Methods
@Override
double getMaxItemInternal() { return maxDoubleItem; }
@Override
public double getMaxItem() {
if (isEmpty() || Double.isNaN(maxDoubleItem)) { throw new SketchesArgumentException(EMPTY_MSG); }
return maxDoubleItem;
}
@Override
String getMaxItemAsString() {
return Double.toString(maxDoubleItem);
}
@Override
double getMinItemInternal() { return minDoubleItem; }
@Override
public double getMinItem() {
if (isEmpty() || Double.isNaN(minDoubleItem)) { throw new SketchesArgumentException(EMPTY_MSG); }
return minDoubleItem;
}
@Override
String getMinItemAsString() {
return Double.toString(minDoubleItem);
}
@Override
byte[] getMinMaxByteArr() {
final byte[] bytesOut = new byte[2 * Double.BYTES];
putDoubleLE(bytesOut, 0, minDoubleItem);
putDoubleLE(bytesOut, Double.BYTES, maxDoubleItem);
return bytesOut;
}
@Override
void setMaxItem(final double item) { maxDoubleItem = item; }
@Override
void setMinItem(final double item) { minDoubleItem = item; }
//END MinMax Methods
@Override
public long getN() { return n; }
//other restricted
@Override
double[] getDoubleItemsArray() { return doubleItems; }
@Override
double getDoubleSingleItem() {
if (n != 1L) { throw new SketchesArgumentException(NOT_SINGLE_ITEM_MSG); }
return doubleItems[k - 1];
}
@Override
int getM() { return m; }
@Override
int getMinK() { return minK; }
@Override
byte[] getRetainedItemsByteArr() {
if (isEmpty()) { return new byte[0]; }
final byte[] bytesOut;
if (isSingleItem()) {
bytesOut = new byte[Double.BYTES];
putDoubleLE(bytesOut, 0, getDoubleSingleItem());
return bytesOut;
}
final int retained = getNumRetained();
final int bytes = retained * Double.BYTES;
bytesOut = new byte[bytes];
final MemorySegment wseg = MemorySegment.ofArray(bytesOut);
MemorySegment.copy(doubleItems, levelsArr[0], wseg, JAVA_DOUBLE_UNALIGNED, 0, retained);
return bytesOut;
}
@Override
byte[] getTotalItemsByteArr() {
final byte[] byteArr = new byte[doubleItems.length * Double.BYTES];
final MemorySegment wseg = MemorySegment.ofArray(byteArr);
MemorySegment.copy(doubleItems, 0, wseg, JAVA_DOUBLE_UNALIGNED, 0, doubleItems.length);
return byteArr;
}
@Override
MemorySegment getMemorySegment() {
return null;
}
@Override
void incN(final int increment) { n += increment; }
@Override
void incNumLevels() {
//the heap sketch computes num levels from the array itself, so this is not used on-heap
}
@Override
boolean isLevelZeroSorted() { return isLevelZeroSorted; }
@Override
void setDoubleItemsArray(final double[] doubleItems) { this.doubleItems = doubleItems; }
@Override
void setDoubleItemsArrayAt(final int index, final double item) { doubleItems[index] = item; }
@Override
void setDoubleItemsArrayAt(final int dstIndex, final double[] srcItems, final int srcOffset, final int length) {
System.arraycopy(srcItems, srcOffset, doubleItems, dstIndex, length);
}
@Override
void setLevelZeroSorted(final boolean sorted) { isLevelZeroSorted = sorted; }
@Override
void setMinK(final int minK) { this.minK = minK; }
@Override
void setN(final long n) { this.n = n; }
@Override
void setNumLevels(final int numLevels) {
//the heap sketch computes num levels from the array itself, so this is not used on-heap
}
@Override
double[] getDoubleRetainedItemsArray() {
return Arrays.copyOfRange(doubleItems, levelsArr[0], levelsArr[getNumLevels()]);
}
@Override
void setMemorySegment(final MemorySegment wseg) { /* heap does not have MemorySegment */ }
@Override
public boolean hasMemorySegment() {
return false;
}
@Override
public boolean isOffHeap() {
return false;
}
@Override
public boolean isSameResource(final MemorySegment that) {
return false;
}
@Override
MemorySegmentRequest getMemorySegmentRequest() {
return null;
}
}