/**
* (C) Copyright IBM Corp. 2010, 2015
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ibm.bi.dml.runtime.util;
import java.util.ArrayList;
/**
* This native long long - double hashmap is specifically designed for
* ctable operations which only require addvalue - extract semantics.
* In contrast to a default hashmap the native representation allows us
* to be more memory-efficient which is important for large maps in order
* to keep data in the caches and prevent high-latency random memory access.
*
*/
public class LongLongDoubleHashMap
{
private static final int INIT_CAPACITY = 8;
private static final int RESIZE_FACTOR = 2;
private static final float LOAD_FACTOR = 0.75f;
private LLDoubleEntry[] data = null;
private int size = -1;
public LongLongDoubleHashMap()
{
data = new LLDoubleEntry[INIT_CAPACITY];
size = 0;
}
public int size() {
return size;
}
/**
*
* @param key1
* @param key2
* @param value
*/
public void addValue(long key1, long key2, double value)
{
//compute entry index position
int hash = hash(key1, key2);
int ix = indexFor(hash, data.length);
//find existing entry and add value
for( LLDoubleEntry e = data[ix]; e!=null; e = e.next ) {
if( e.key1==key1 && e.key2==key2 ) {
e.value += value;
return; //no need to append or resize
}
}
//add non-existing entry (constant time)
LLDoubleEntry enew = new LLDoubleEntry(key1, key2, value);
enew.next = data[ix]; //colliding entries / null
data[ix] = enew;
size++;
//resize if necessary
if( size >= LOAD_FACTOR*data.length )
resize();
}
/**
*
* @return
*/
public ArrayList<LLDoubleEntry> extractValues()
{
ArrayList<LLDoubleEntry> ret = new ArrayList<LLDoubleEntry>();
for( LLDoubleEntry e : data ) {
if( e != null ) {
while( e.next!=null ) {
ret.add(e);
e = e.next;
}
ret.add(e);
}
}
return ret;
}
/**
*
*/
private void resize() {
//check for integer overflow on resize
if( data.length > Integer.MAX_VALUE/RESIZE_FACTOR )
return;
//resize data array and copy existing contents
LLDoubleEntry[] olddata = data;
data = new LLDoubleEntry[data.length*RESIZE_FACTOR];
size = 0;
//rehash all entries
for( LLDoubleEntry e : olddata ) {
if( e != null ) {
while( e.next!=null ) {
addValue(e.key1, e.key2, e.value);
e = e.next;
}
addValue(e.key1, e.key2, e.value);
}
}
}
/**
*
* @param key1
* @param key2
* @return
*/
private static int hash(long key1, long key2) {
//basic hash mixing of two longs hashes (w/o object creation)
int h = (int)(key1 ^ (key1 >>> 32));
h = h*31 + (int)(key2 ^ (key2 >>> 32));
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
*
* @param h
* @param length
* @return
*/
private static int indexFor(int h, int length) {
return h & (length-1);
}
/**
*
*/
public class LLDoubleEntry {
public long key1 = Long.MAX_VALUE;
public long key2 = Long.MAX_VALUE;
public double value = Double.MAX_VALUE;
public LLDoubleEntry next = null;
public LLDoubleEntry(long k1, long k2, double val) {
key1 = k1;
key2 = k2;
value = val;
next = null;
}
}
}