package be.bagofwords.db.data;
import be.bagofwords.util.ByteArraySerializable;
import be.bagofwords.util.HashUtils;
import be.bagofwords.util.Pair;
import be.bagofwords.util.SerializationUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DoubleCountsList extends ArrayList<Pair<Long, Double>> implements ByteArraySerializable {
public DoubleCountsList() {
super();
}
public DoubleCountsList(DoubleCountsList list) {
super(list);
}
public DoubleCountsList(long key, int count) {
this();
addCount(key, count);
}
public DoubleCountsList(int initialSize) {
super(initialSize);
}
public DoubleCountsList(byte[] serialized) {
int longWidth = SerializationUtils.getWidth(Long.class);
int size = serialized.length / (longWidth * 2);
int offset = 0;
for (int i = 0; i < size; i++) {
long key = SerializationUtils.bytesToLong(serialized, offset);
double value = Double.longBitsToDouble(SerializationUtils.bytesToLong(serialized, serialized.length / 2 + offset));
offset += longWidth;
add(new Pair<>(key, value));
}
}
public synchronized double getCount(long key) {
int ind = Collections.binarySearch((List) this, key);
if (ind >= 0) {
return get(ind).getSecond();
} else {
return 0;
}
}
public synchronized void addCount(long key, double count) {
add(new Pair<>(key, count));
}
@JsonIgnore
public synchronized double getTotal() {
double total = 0;
for (Pair<Long, Double> value : this) {
total += value.getSecond();
}
return total;
}
public synchronized DoubleCountsList clone() {
return new DoubleCountsList(this);
}
public void compact() {
if (!isCompacted()) {
synchronized (this) {
if (!isCompacted()) {
List<Pair<Long, Double>> oldCounts = new ArrayList<>(this);
clear();
Collections.sort(oldCounts);
for (int i = 0; i < oldCounts.size(); ) {
Pair<Long, Double> curr = oldCounts.get(i);
double combinedCount = curr.getSecond();
i++;
while (i < oldCounts.size() && oldCounts.get(i).getFirst().equals(curr.getFirst())) {
combinedCount += oldCounts.get(i++).getSecond();
}
add(new Pair<>(curr.getFirst(), combinedCount));
}
}
}
}
}
private boolean isCompacted() {
for (int i = 1; i < size(); i++) {
if (get(i - 1).getFirst() >= get(i).getFirst()) {
return false;
}
}
return true;
}
public synchronized int hashCode() {
int result = HashUtils.startHash;
for (Pair<Long, Double> value : this) {
result = result * HashUtils.addHash + value.getFirst().intValue();
result = result * HashUtils.addHash + value.getSecond().hashCode();
}
return result;
}
public String toString() {
String result = "{ ";
int i;
for (i = 0; i < size() && i < 10; i++) {
result += get(i).getFirst() + ":" + get(i).getSecond();
if (i < size() - 1) {
result += ", ";
}
}
if (i < size()) {
result += " ...";
}
result += "}";
return result;
}
public List<Long> getSortedKeys() {
List<Pair<Long, Double>> sortedValues = new ArrayList<>(this);
Collections.sort(sortedValues, (val1, val2) -> {
if (val1.getSecond().equals(val2.getSecond())) {
return Long.compare(val1.getFirst(), val2.getFirst());
} else {
return -Double.compare(val1.getSecond(), val2.getSecond()); //largest first
}
});
List<Long> result = new ArrayList<>();
for (Pair<Long, Double> value : sortedValues) {
result.add(value.getFirst());
}
return result;
}
public synchronized byte[] toByteArray() {
int longWidth = SerializationUtils.getWidth(Long.class);
int size = size() * longWidth * 2;
byte[] result = new byte[size];
int offset = 0;
for (Pair<Long, Double> value : this) {
SerializationUtils.longToBytes(value.getFirst(), result, offset);
offset += longWidth;
}
for (Pair<Long, Double> value : this) {
SerializationUtils.longToBytes(Double.doubleToLongBits(value.getSecond()), result, offset);
offset += longWidth;
}
if (offset != result.length) {
throw new RuntimeException("Something went wrong!");
}
return result;
}
}