hll/hll_6array.go (165 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"
"github.com/apache/datasketches-go/internal"
)
// hll6ArrayImpl Uses 6 bits per slot in a packed byte array
type hll6ArrayImpl struct {
hllArrayImpl
}
type hll6Iterator struct {
hllPairIterator
hll *hll6ArrayImpl
bitOffset int
}
func (h *hll6ArrayImpl) iterator() pairIterator {
a := newHll6Iterator(1<<h.lgConfigK, h)
return &a
}
func (h *hll6ArrayImpl) copyAs(tgtHllType TgtHllType) (hllSketchStateI, error) {
if tgtHllType == h.tgtHllType {
return h.copy()
}
if tgtHllType == TgtHllTypeHll4 {
return convertToHll4(h)
}
if tgtHllType == TgtHllTypeHll8 {
return convertToHll8(h)
}
return nil, fmt.Errorf("cannot convert to TgtHllType id: %d", int(tgtHllType))
}
func (h *hll6ArrayImpl) copy() (hllSketchStateI, error) {
return &hll6ArrayImpl{
hllArrayImpl: h.copyCommon(),
}, nil
}
func (h *hll6ArrayImpl) ToCompactSlice() ([]byte, error) {
return h.ToUpdatableSlice()
}
func (h *hll6ArrayImpl) ToUpdatableSlice() ([]byte, error) {
return toHllByteArr(h, false)
}
// newHll6Array returns a new Hll4Array.
func newHll6Array(lgConfigK int) hllArray {
return &hll6ArrayImpl{
hllArrayImpl: hllArrayImpl{
hllSketchConfig: newHllSketchConfig(lgConfigK, TgtHllTypeHll6, curModeHll),
curMin: 0,
numAtCurMin: 1 << lgConfigK,
hipAccum: 0,
kxq0: float64(uint64(1 << lgConfigK)),
kxq1: 0,
hllByteArr: make([]byte, (((1<<lgConfigK)*3)>>2)+1),
auxStart: hllByteArrStart + 1<<(lgConfigK-1),
},
}
}
// deserializeHll6 returns a new Hll6Array from the given byte array.
func deserializeHll6(byteArray []byte) hllArray {
lgConfigK := extractLgK(byteArray)
hll6 := newHll6Array(lgConfigK)
hll6.extractCommonHll(byteArray)
return hll6
}
func (h *hll6ArrayImpl) couponUpdate(coupon int) (hllSketchStateI, error) {
newValue := coupon >> keyBits26
slotNo := coupon & h.slotNoMask
err := h.updateSlotWithKxQ(slotNo, newValue)
return h, err
}
func (h *hll6ArrayImpl) updateSlotWithKxQ(slotNo int, newValue int) error {
oldValue := h.getSlotValue(slotNo)
if newValue > oldValue {
put6Bit(h.hllByteArr, 0, slotNo, newValue)
err := h.hipAndKxQIncrementalUpdate(oldValue, newValue)
if err != nil {
return err
}
if oldValue == 0 {
h.numAtCurMin-- //interpret numAtCurMin as num Zeros
if h.numAtCurMin < 0 {
return fmt.Errorf("numAtCurMin < 0")
}
}
}
return nil
}
func (h *hll6ArrayImpl) getSlotValue(slotNo int) int {
return get6Bit(h.hllByteArr, 0, slotNo)
}
func get6Bit(arr []byte, offsetBytes int, slotNo int) int {
startBit := slotNo * 6
shift := startBit & 0x7
byteIdx := (startBit >> 3) + offsetBytes
return (internal.GetShortLE(arr, byteIdx) >> shift) & 0x3F
}
func put6Bit(arr []byte, offsetBytes int, slotNo int, newValue int) {
startBit := slotNo * 6
shift := startBit & 0x7
byteIdx := (startBit >> 3) + offsetBytes
valShifted := (newValue & 0x3F) << shift
curMasked := internal.GetShortLE(arr, byteIdx) & (^(valMask6 << shift))
insert := curMasked | valShifted
internal.PutShortLE(arr, byteIdx, insert)
}
func convertToHll6(srcAbsHllArr hllArray) (hllSketchStateI, error) {
lgConfigK := srcAbsHllArr.GetLgConfigK()
hll6Array := newHll6Array(lgConfigK)
hll6Array.putOutOfOrder(srcAbsHllArr.isOutOfOrder())
numZeros := 1 << lgConfigK
srcItr := srcAbsHllArr.iterator()
for srcItr.nextAll() {
v, err := srcItr.getValue()
if err != nil {
return nil, err
}
if v != empty {
numZeros--
p, err := srcItr.getPair()
if err != nil {
return nil, err
}
_, err = hll6Array.couponUpdate(p) //couponUpdate creates KxQ registers
if err != nil {
return nil, err
}
}
}
hll6Array.putNumAtCurMin(numZeros)
hll6Array.putHipAccum(srcAbsHllArr.getHipAccum()) //intentional overwrite
hll6Array.putRebuildCurMinNumKxQFlag(false)
return hll6Array, nil
}
func newHll6Iterator(lengthPairs int, hll *hll6ArrayImpl) hll6Iterator {
return hll6Iterator{
hllPairIterator: newHllPairIterator(lengthPairs),
hll: hll,
bitOffset: -6,
}
}
func (h *hll6Iterator) nextAll() bool {
h.index++
if h.index >= h.lengthPairs {
return false
}
h.bitOffset += 6
return true
}
func (h *hll6Iterator) nextValid() bool {
for h.index+1 < h.lengthPairs {
h.index++
h.bitOffset += 6
tmp := internal.GetShortLE(h.hll.hllByteArr, h.bitOffset/8)
h.value = (tmp >> ((h.bitOffset % 8) & 0x7)) & valMask6
if h.value != empty {
return true
}
}
return false
}
func (h *hll6Iterator) getValue() (int, error) {
tmp := internal.GetShortLE(h.hll.hllByteArr, h.bitOffset/8)
shift := (h.bitOffset % 8) & 0x7
return (tmp >> shift) & valMask6, nil
}
func (h *hll6Iterator) getPair() (int, error) {
v, err := h.getValue()
return pair(h.index, v), err
}