package at.yawk.numaec;
import org.eclipse.collections.api.LongIterable;
import org.eclipse.collections.api.bag.MutableBag;
import org.eclipse.collections.api.bag.primitive.MutableFloatBag;
import org.eclipse.collections.api.block.function.primitive.FloatFunction;
import org.eclipse.collections.api.block.function.primitive.FloatFunction0;
import org.eclipse.collections.api.block.function.primitive.FloatToFloatFunction;
import org.eclipse.collections.api.block.function.primitive.FloatToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.LongFloatToFloatFunction;
import org.eclipse.collections.api.block.function.primitive.LongToFloatFunction;
import org.eclipse.collections.api.block.predicate.primitive.FloatPredicate;
import org.eclipse.collections.api.block.predicate.primitive.LongFloatPredicate;
import org.eclipse.collections.api.iterator.MutableFloatIterator;
import org.eclipse.collections.api.map.primitive.MutableFloatLongMap;
import org.eclipse.collections.api.map.primitive.MutableLongFloatMap;
import org.eclipse.collections.api.map.primitive.LongFloatMap;
class LongFloatLinearHashMap extends BaseLongFloatMap implements LongFloatBufferMap {
private final float loadFactor;
private final long sipHashK0, sipHashK1;
private final long hashMask;
protected final LinearHashTable table;
protected int size;
LongFloatLinearHashMap(LargeByteBufferAllocator allocator, LinearHashMapConfig config) {
this.sipHashK0 = config.sipHashK0.getAsLong();
this.sipHashK1 = config.sipHashK1.getAsLong();
this.loadFactor = config.loadFactor;
int hashLength = config.hashLength;
this.hashMask = hashLength == 0 ? -1L : ~(-1L >>> hashLength);
this.table = new LinearHashTable(allocator, config, hashLength + Long.BYTES + Float.BYTES) {
@Override
protected void write(LargeByteBuffer lbb, long address, long hash, long key, long value) {
if (hashLength != 0) {
if ((hash & ~hashMask) != 0) {
throw new AssertionError();
}
BTree.uset(lbb, address, hashLength, Long.reverse(hash));
}
lbb.setLong(address + hashLength, fromKey(key));
lbb.setFloat(address + hashLength + Long.BYTES, fromValue(value));
}
@Override
protected long readHash(LargeByteBuffer lbb, long address) {
if (hashLength == 0) {
return hash(fromKey(readKey(lbb, address)));
} else {
return Long.reverse(BTree.uget(lbb, address, hashLength));
}
}
@Override
protected long readKey(LargeByteBuffer lbb, long address) {
return toKey(lbb.getLong(address + hashLength));
}
@Override
protected long readValue(LargeByteBuffer lbb, long address) {
return toValue(lbb.getFloat(address + hashLength + Long.BYTES));
}
};
}
protected void ensureCapacity(int capacity) {
table.expandToFullLoadCapacity((long) (capacity / loadFactor));
}
@Override
protected MapStoreCursor iterationCursor() {
return table.allocateCursor();
}
@Override
protected MapStoreCursor keyCursor(long key) {
LinearHashTable.Cursor cursor = table.allocateCursor();
cursor.seek(hash(key), toKey(key));
return cursor;
}
@DoNotMutate
@Override
void checkInvariants() {
super.checkInvariants();
table.checkInvariants();
}
protected long hash(long key) {
return SipHash.sipHash2_4_8_to_8(sipHashK0, sipHashK1, toKey(key)) & hashMask;
}
@Override
public void close() {
table.close();
}
@Override
public int size() {
return size;
}
public static class Mutable extends LongFloatLinearHashMap implements MutableLongFloatBufferMap {
Mutable(LargeByteBufferAllocator allocator, LinearHashMapConfig config) {
super(allocator, config);
}
@Override
public void put(long key, float value) {
ensureCapacity(1);
long h = hash(key);
long k = toKey(key);
long v = toValue(value);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
cursor.seek(h, k);
if (cursor.elementFound()) {
cursor.setValue(v);
} else {
cursor.insert(h, k, v);
size++;
ensureCapacity(size);
}
}
}
@Override
public void putAll(LongFloatMap map) {
ensureCapacity(size + map.size());
map.forEachKeyValue(this::put);
}
@Override
public void updateValues(LongFloatToFloatFunction function) {
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
while (cursor.next()) {
float updated = function.valueOf(fromKey(cursor.getKey()), fromValue(cursor.getValue()));
cursor.setValue(toValue(updated));
}
}
}
@Override
public void removeKey(long key) {
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
cursor.seek(hash(key), toKey(key));
if (cursor.elementFound()) {
cursor.remove();
size--;
}
}
}
@Override
public void remove(long key) {
removeKey(key);
}
@Override
public float removeKeyIfAbsent(long key, float value) {
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
cursor.seek(hash(key), toKey(key));
if (cursor.elementFound()) {
float v = fromValue(cursor.getValue());
cursor.remove();
size--;
return v;
} else {
return value;
}
}
}
@Override
public float getIfAbsentPut(long key, float value) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
return fromValue(cursor.getValue());
} else {
cursor.insert(hash, toKey(key), toValue(value));
size++;
ensureCapacity(size);
return value;
}
}
}
@Override
public float getIfAbsentPut(long key, FloatFunction0 function) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
return fromValue(cursor.getValue());
} else {
float v = function.value();
cursor.insert(hash, toKey(key), toValue(v));
size++;
ensureCapacity(size);
return v;
}
}
}
@Override
public float getIfAbsentPutWithKey(long key, LongToFloatFunction function) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
return fromValue(cursor.getValue());
} else {
float v = function.valueOf(key);
cursor.insert(hash, toKey(key), toValue(v));
size++;
ensureCapacity(size);
return v;
}
}
}
@Override
public <P> float getIfAbsentPutWith(long key, FloatFunction<? super P> function, P parameter) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
return fromValue(cursor.getValue());
} else {
float v = function.floatValueOf(parameter);
cursor.insert(hash, toKey(key), toValue(v));
size++;
ensureCapacity(size);
return v;
}
}
}
@Override
public float updateValue(long key, float initialValueIfAbsent, FloatToFloatFunction function) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
float updated = function.valueOf(fromValue(cursor.getValue()));
cursor.setValue(toValue(updated));
return updated;
} else {
float updated = function.valueOf(initialValueIfAbsent);
cursor.insert(hash, toKey(key), toValue(updated));
size++;
ensureCapacity(size);
return updated;
}
}
}
@Override
public MutableLongFloatMap withKeyValue(long key, float value) {
put(key, value);
return this;
}
@Override
public MutableLongFloatMap withoutKey(long key) {
removeKey(key);
return this;
}
@Override
public MutableLongFloatMap withoutAllKeys(LongIterable keys) {
keys.forEach(this::removeKey);
return this;
}
@Override
public MutableLongFloatMap asUnmodifiable() {
throw new UnsupportedOperationException("Mutable.asUnmodifiable not implemented yet");
}
@Override
public MutableLongFloatMap asSynchronized() {
throw new UnsupportedOperationException("Mutable.asSynchronized not implemented yet");
}
@Override
public float addToValue(long key, float toBeAdded) {
ensureCapacity(1);
try (LinearHashTable.Cursor cursor = table.allocateCursor()) {
long hash = hash(key);
cursor.seek(hash, toKey(key));
if (cursor.elementFound()) {
float updated = (float) (fromValue(cursor.getValue()) + toBeAdded);
cursor.setValue(toValue(updated));
return updated;
} else {
cursor.insert(hash, toKey(key), toValue(toBeAdded));
size++;
ensureCapacity(size);
return toBeAdded;
}
}
}
@Override
public void clear() {
table.clear();
size = 0;
}
@Override
public MutableFloatLongMap flipUniqueValues() {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.flipUniqueValues not implemented yet");
}
@Override
public MutableLongFloatMap select(LongFloatPredicate predicate) {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.select not implemented yet");
}
@Override
public MutableFloatBag select(FloatPredicate predicate) {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.select not implemented yet");
}
@Override
public MutableLongFloatMap reject(LongFloatPredicate predicate) {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.reject not implemented yet");
}
@Override
public MutableFloatBag reject(FloatPredicate predicate) {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.reject not implemented yet");
}
@Override
public <V> MutableBag<V> collect(FloatToObjectFunction<? extends V> function) {
throw new UnsupportedOperationException("LongFloatBufferMap.Mutable.collect not implemented yet");
}
@Override
public MutableFloatIterator floatIterator() {
return super.floatIterator();
}
}
}