hll/hll_4array.go (169 lines of code) (raw):
/*
* 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 hll
import (
"fmt"
)
// hll4ArrayImpl Uses 4 bits per slot in a packed byte array
type hll4ArrayImpl struct {
hllArrayImpl
}
func (h *hll4ArrayImpl) getSlotValue(slotNo int) (int, error) {
nib := h.getNibble(slotNo)
if nib == auxToken {
auxHashMap := h.getAuxHashMap()
return auxHashMap.mustFindValueFor(slotNo)
} else {
return nib + h.curMin, nil
}
}
type hll4Iterator struct {
hllPairIterator
hll *hll4ArrayImpl
}
func (h *hll4ArrayImpl) iterator() pairIterator {
a := newHll4Iterator(1<<h.lgConfigK, h)
return &a
}
func (h *hll4ArrayImpl) ToCompactSlice() ([]byte, error) {
return toHllByteArr(h, true)
}
func (h *hll4ArrayImpl) ToUpdatableSlice() ([]byte, error) {
return toHllByteArr(h, false)
}
func (h *hll4ArrayImpl) GetUpdatableSerializationBytes() int {
auxHashMap := h.getAuxHashMap()
auxBytes := 0
if auxHashMap == nil {
auxBytes = 4 << lgAuxArrInts[h.lgConfigK]
} else {
auxBytes = 4 << auxHashMap.getLgAuxArrInts()
}
return hllByteArrStart + h.getHllByteArrBytes() + auxBytes
}
func (h *hll4ArrayImpl) copyAs(tgtHllType TgtHllType) (hllSketchStateI, error) {
if tgtHllType == h.tgtHllType {
return h.copy()
}
if tgtHllType == TgtHllTypeHll6 {
return convertToHll6(h)
}
if tgtHllType == TgtHllTypeHll8 {
return convertToHll8(h)
}
return nil, fmt.Errorf("cannot convert to TgtHllType id: %d ", int(tgtHllType))
}
func (h *hll4ArrayImpl) copy() (hllSketchStateI, error) {
return &hll4ArrayImpl{
hllArrayImpl: h.copyCommon(),
}, nil
}
// newHll4Array returns a new Hll4Array.
func newHll4Array(lgConfigK int) hllArray {
return &hll4ArrayImpl{
hllArrayImpl: hllArrayImpl{
hllSketchConfig: newHllSketchConfig(lgConfigK, TgtHllTypeHll4, curModeHll),
curMin: 0,
numAtCurMin: 1 << lgConfigK,
hipAccum: 0,
kxq0: float64(uint64(1 << lgConfigK)),
kxq1: 0,
hllByteArr: make([]byte, 1<<(lgConfigK-1)),
auxStart: hllByteArrStart + 1<<(lgConfigK-1),
},
}
}
// deserializeHll4 returns a new Hll4Array from the given byte array.
func deserializeHll4(byteArray []byte) (hllArray, error) {
lgConfigK := extractLgK(byteArray)
hll4 := newHll4Array(lgConfigK)
hll4.extractCommonHll(byteArray)
auxStart := hll4.getAuxStart()
auxCount := extractAuxCount(byteArray)
compact := extractCompactFlag(byteArray)
if auxCount > 0 {
auxHashMap, err := deserializeAuxHashMap(byteArray, auxStart, lgConfigK, auxCount, compact)
if err != nil {
return nil, err
}
hll4.putAuxHashMap(auxHashMap, false)
}
return hll4, nil
}
func convertToHll4(srcAbsHllArr hllArray) (hllSketchStateI, error) {
lgConfigK := srcAbsHllArr.GetLgConfigK()
hll4Array := newHll4Array(lgConfigK)
hll4Array.putOutOfOrder(srcAbsHllArr.isOutOfOrder())
// 1st pass: compute starting curMin and numAtCurMin:
pair, err := curMinAndNum(srcAbsHllArr)
if err != nil {
return nil, err
}
curMin := getPairValue(pair)
numAtCurMin := getPairLow26(pair)
// 2nd pass: Must know curMin to create auxHashMap.
// Populate KxQ registers, build auxHashMap if needed
srcItr := srcAbsHllArr.iterator()
auxHashMap := hll4Array.getAuxHashMap() //may be null
for srcItr.nextValid() {
slotNo := srcItr.getIndex()
actualValue, err := srcItr.getValue()
if err != nil {
return nil, err
}
err = hll4Array.hipAndKxQIncrementalUpdate(0, actualValue)
if err != nil {
return nil, err
}
if actualValue >= (curMin + 15) {
hll4Array.putNibble(slotNo, auxToken)
if auxHashMap == nil {
auxHashMap = newAuxHashMap(lgAuxArrInts[lgConfigK], lgConfigK)
hll4Array.putAuxHashMap(auxHashMap, false)
}
err := auxHashMap.mustAdd(slotNo, actualValue)
if err != nil {
return nil, err
}
} else {
hll4Array.putNibble(slotNo, byte(actualValue-curMin))
}
}
hll4Array.putCurMin(curMin)
hll4Array.putNumAtCurMin(numAtCurMin)
hll4Array.putHipAccum(srcAbsHllArr.getHipAccum()) //intentional overwrite
hll4Array.putRebuildCurMinNumKxQFlag(false)
return hll4Array, nil
}
// couponUpdate updates the Hll4Array with the given coupon and returns the updated Hll4Array.
func (h *hll4ArrayImpl) couponUpdate(coupon int) (hllSketchStateI, error) {
newValue := coupon >> keyBits26
slotNo := coupon & h.slotNoMask
err := internalHll4Update(h, slotNo, newValue)
return h, err
}
func curMinAndNum(absHllArr hllArray) (int, error) {
curMin := 64
numAtCurMin := 0
itr := absHllArr.iterator()
for itr.nextAll() {
v, err := itr.getValue()
if err != nil {
return 0, err
}
if v > curMin {
continue
}
if v < curMin {
curMin = v
numAtCurMin = 1
} else {
numAtCurMin++
}
}
return pair(numAtCurMin, curMin), nil
}
func newHll4Iterator(lengthPairs int, hll *hll4ArrayImpl) hll4Iterator {
return hll4Iterator{
hllPairIterator: newHllPairIterator(lengthPairs),
hll: hll,
}
}
func (itr *hll4Iterator) getValue() (int, error) {
return itr.hll.getSlotValue(itr.getIndex())
}
func (itr *hll4Iterator) getPair() (int, error) {
v, err := itr.getValue()
return pair(itr.index, v), err
}