/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.epl.approx;
import java.nio.ByteBuffer;
import java.util.*;
public class CountMinSketchStateTopk {
private final int topkMax;
// Wherein: Object either is ByteBuffer or Deque<ByteBuffer>
private final TreeMap<Long, Object> topk;
private final Map<ByteBuffer, Long> lastFreqForItem;
public CountMinSketchStateTopk(int topkMax) {
this.topkMax = topkMax;
this.lastFreqForItem = new HashMap<ByteBuffer, Long>();
this.topk = new TreeMap<Long, Object>(Collections.reverseOrder());
}
public CountMinSketchStateTopk(int topkMax, TreeMap<Long, Object> topk, Map<ByteBuffer, Long> lastFreqForItem) {
this.topkMax = topkMax;
this.topk = topk;
this.lastFreqForItem = lastFreqForItem;
}
public TreeMap<Long, Object> getTopk() {
return topk;
}
public void updateExpectIncreasing(byte[] value, long frequency) {
boolean filled = lastFreqForItem.size() == topkMax;
if (!filled) {
ByteBuffer valueBuffer = ByteBuffer.wrap(value);
updateInternal(valueBuffer, frequency);
} else {
Long lastKey = topk.lastKey();
if (frequency > lastKey) {
ByteBuffer valueBuffer = ByteBuffer.wrap(value);
updateInternal(valueBuffer, frequency);
}
}
trimItems();
}
private void updateInternal(ByteBuffer valueBuffer, long frequency) {
Long beforeUpdateFrequency = lastFreqForItem.put(valueBuffer, frequency);
if (beforeUpdateFrequency != null) {
removeItemFromSorted(beforeUpdateFrequency, valueBuffer);
}
addItemToSorted(frequency, valueBuffer);
}
private void removeItemFromSorted(long frequency, ByteBuffer value) {
Object existing = topk.get(frequency);
if (existing != null) {
if (existing instanceof Deque) {
Deque<ByteBuffer> deque = (Deque<ByteBuffer>) existing;
deque.remove(value);
if (deque.isEmpty()) {
topk.remove(frequency);
}
} else {
topk.remove(frequency);
}
}
}
private void addItemToSorted(long frequency, ByteBuffer value) {
Object existing = topk.get(frequency);
if (existing == null) {
topk.put(frequency, value);
} else if (existing instanceof Deque) {
Deque<ByteBuffer> deque = (Deque<ByteBuffer>) existing;
deque.add(value);
} else {
Deque<ByteBuffer> deque = new ArrayDeque<ByteBuffer>(2);
deque.add((ByteBuffer) existing);
deque.add(value);
topk.put(frequency, deque);
}
}
private void trimItems() {
while (lastFreqForItem.size() > topkMax) {
Map.Entry<Long, Object> last = topk.lastEntry();
if (last == null) {
break;
}
if (last.getValue() instanceof Deque) {
Deque<ByteBuffer> deque = (Deque<ByteBuffer>) last.getValue();
ByteBuffer valueRemoved = deque.removeLast();
lastFreqForItem.remove(valueRemoved);
if (deque.isEmpty()) {
topk.remove(last.getKey());
}
} else {
topk.remove(last.getKey());
lastFreqForItem.remove((ByteBuffer) last.getValue());
}
}
}
public List<ByteBuffer> getTopKValues() {
List<ByteBuffer> values = new ArrayList<ByteBuffer>();
for (Map.Entry<Long, Object> entry : topk.entrySet()) {
if (entry.getValue() instanceof Deque) {
Deque<ByteBuffer> set = (Deque<ByteBuffer>) entry.getValue();
for (ByteBuffer o : set) {
values.add(o);
}
} else {
values.add((ByteBuffer) entry.getValue());
}
}
return values;
}
public int getTopkMax() {
return topkMax;
}
}