package org.apache.lucene.codecs;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import org.apache.lucene.index.Impact;
public final class CompetitiveImpactAccumulator {
private final int[] maxFreqs;
private final TreeSet<Impact> otherFreqNormPairs;
public CompetitiveImpactAccumulator() {
maxFreqs = new int[256];
Comparator<Impact> comparator = new Comparator<Impact>() {
@Override
public int compare(Impact o1, Impact o2) {
int cmp = Integer.compare(o1.freq, o2.freq);
if (cmp == 0) {
cmp = Long.compareUnsigned(o2.norm, o1.norm);
}
return cmp;
}
};
otherFreqNormPairs = new TreeSet<>(comparator);
}
public void clear() {
Arrays.fill(maxFreqs, 0);
otherFreqNormPairs.clear();
assert assertConsistent();
}
public void add(int freq, long norm) {
if (norm >= Byte.MIN_VALUE && norm <= Byte.MAX_VALUE) {
int index = Byte.toUnsignedInt((byte) norm);
maxFreqs[index] = Math.max(maxFreqs[index], freq);
} else {
add(new Impact(freq, norm), otherFreqNormPairs);
}
assert assertConsistent();
}
public void addAll(CompetitiveImpactAccumulator acc) {
int[] maxFreqs = this.maxFreqs;
int[] otherMaxFreqs = acc.maxFreqs;
for (int i = 0; i < maxFreqs.length; ++i) {
maxFreqs[i] = Math.max(maxFreqs[i], otherMaxFreqs[i]);
}
for (Impact entry : acc.otherFreqNormPairs) {
add(entry, otherFreqNormPairs);
}
assert assertConsistent();
}
public Collection<Impact> getCompetitiveFreqNormPairs() {
List<Impact> impacts = new ArrayList<>();
int maxFreqForLowerNorms = 0;
for (int i = 0; i < maxFreqs.length; ++i) {
int maxFreq = maxFreqs[i];
if (maxFreq > maxFreqForLowerNorms) {
impacts.add(new Impact(maxFreq, (byte) i));
maxFreqForLowerNorms = maxFreq;
}
}
if (otherFreqNormPairs.isEmpty()) {
return impacts;
}
TreeSet<Impact> freqNormPairs = new TreeSet<>(this.otherFreqNormPairs);
for (Impact impact : impacts) {
add(impact, freqNormPairs);
}
return Collections.unmodifiableSet(freqNormPairs);
}
private void add(Impact newEntry, TreeSet<Impact> freqNormPairs) {
Impact next = freqNormPairs.ceiling(newEntry);
if (next == null) {
freqNormPairs.add(newEntry);
} else if (Long.compareUnsigned(next.norm, newEntry.norm) <= 0) {
return;
} else {
freqNormPairs.add(newEntry);
}
for (Iterator<Impact> it = freqNormPairs.headSet(newEntry, false).descendingIterator(); it.hasNext(); ) {
Impact entry = it.next();
if (Long.compareUnsigned(entry.norm, newEntry.norm) >= 0) {
it.remove();
} else {
break;
}
}
}
@Override
public String toString() {
return new ArrayList<>(getCompetitiveFreqNormPairs()).toString();
}
private boolean assertConsistent() {
for (int freq : maxFreqs) {
assert freq >= 0;
}
int previousFreq = 0;
long previousNorm = 0;
for (Impact impact : otherFreqNormPairs) {
assert impact.norm < Byte.MIN_VALUE || impact.norm > Byte.MAX_VALUE;
assert previousFreq < impact.freq;
assert Long.compareUnsigned(previousNorm, impact.norm) < 0;
previousFreq = impact.freq;
previousNorm = impact.norm;
}
return true;
}
}