sclass MultiSetAndTopTen {
int maxEntries = 100000, topTenTargetLength = 10;
Map ms;
L topTen;
Set topTenSet;
transient Lock lock = lock();
*() {}
*(Map *ms, L *topTen) { topTenSet = new TreeSet(topTen); }
void add(A a) {
lock lock;
long count = longMultiSet_add(ms, a);
insertToTopTen(a, count);
if (l(ms) >= maxEntries)
longMultiSet_dropBottomPart(ms, l(ms)-iround(maxEntries*0.9));
}
void insertToTopTen(A a, long count) {
int originalIndex = topTenSet.contains(a) ? indexOf(topTen, a) : -1;
int i = originalIndex < 0 ? l(topTen) : originalIndex;
while (i > 0 && count >= toLong(ms.get(topTen.get(i-1)))) // maybe this is even better than binary search as we will usually exit immediately
--i;
if (i >= topTenTargetLength || i == originalIndex) ret;
if (originalIndex >= 0) topTen.remove(originalIndex);
topTen.add(i, a); topTenSet.add(a);
while (l(topTen) > topTenTargetLength)
topTenSet.remove(popLast(topTen));
}
void printTopTen {
printAsciiHeading("TOP TEN (OF " + l(ms) + ")");
pnl(map(topTen, func(S s) { s + " [" + ms.get(s) + "]" }));
}
void setTopTenSize(int size) {
topTenTargetLength = size;
if (l(topTen) != size) newTopTen(size);
}
void newTopTen(int size) {
topTenTargetLength = size;
replaceCollection(topTen, takeFirst(size, sortByDescScore(keys(ms), ms)));
topTenSet = new TreeSet(topTen);
}
int numEntries() { ret l(ms); }
long numOccurrences() { ret longMultiSet_l(ms); }
bool contains(A a) { ret ms.containsKey(a); }
long get(A a) { ret toLong(ms.get(a)); }
L topTen() { lock lock; ret cloneList(topTen); }
L entries() { lock lock; ret asList(keys(ms)); }
void clear { lock lock; ms.clear(); topTen.clear(); topTenSet.clear(); }
}