/*
* Copyright 2015 MiLaboratory.com
*
* 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.milaboratory.util;
/**
* Hashing algorithms. The algorithms was taken from different open sources. <p/> <h3>Links:</h3> <a
* href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
* <a href="http://www.burtleburtle.net/bob/hash/doobs.html">http://www.burtleburtle.net/bob/hash/doobs.html</a><br/> <a
* href="http://bretm.home.comcast.net/~bretm/hash">http://bretm.home.comcast.net/~bretm/hash</a><br/> <a
* href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/> <a
* href="http://en.wikipedia.org/wiki/Jenkins_hash_function's">http://en.wikipedia.org/wiki/Jenkins_hash_function's</a><br/>
* <a href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a><br/> <a
* href="http://dmy999.com/article/50/murmurhash-2-java-port">http://dmy999.com/article/50/murmurhash-2-java-port</a><br/>
* <a href="http://en.wikipedia.org/wiki/MurmurHash">http://en.wikipedia.org/wiki/MurmurHash</a><br/>
*
* @author Dmitry Bolotin
* @author Stanislav Poslavsky
* @since 1.0
*/
public final class HashFunctions {
private HashFunctions() {
}
/**
* Robert Jenkins' 96 bit Mix Function. <p/> Variable 'c' contains the input key. When the mixing is complete,
* variable 'c' also contains the hash result. Variable 'a', and 'b' contain initialized random bits. Notice the
* total number of internal state is 96 bits, much larger than the final output of 32 bits. Also notice the sequence
* of subtractions rolls through variable 'a' to variable 'c' three times. Each row will act on one variable, mixing
* in information from the other two variables, followed by a shift operation. <p/> <p>Subtraction is similar to
* multiplication in that changes in upper bits of the key do not influence lower bits of the addition. The 9 bit
* shift operations in Robert Jenkins' mixing algorithm shifts the key to the right 61 bits in total, and shifts the
* key to the left 34 bits in total. As the calculation is chained, each exclusive-or doubles the number of states.
* There are at least 2^9 different combined versions of the original key, shifted by various amounts. That is why a
* single bit change in the key can influence widely apart bits in the hash result. <p/> <p>The uniform distribution
* of the hash function can be determined from the nature of the subtraction operation. Look at a single bit
* subtraction operation between a key, and a random bit. If the random bit is 0, then the key remains unchanged. If
* the random bit is 1, then the key will be flipped. A carry will occur in the case where both the key bit and the
* random bit are 1. Subtracting the random bits will cause about half of the key bits to be flipped. So even if the
* key is not uniform, subtracting the random bits will result in uniform distribution. <p/> <h3>Links:</h3> <a
* href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
* <a href="http://www.burtleburtle.net/bob/hash/doobs.html">http://www.burtleburtle.net/bob/hash/doobs.html</a><br/>
* <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/> <a
* href="http://en.wikipedia.org/wiki/Jenkins_hash_function's">http://en.wikipedia.org/wiki/Jenkins_hash_function's</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param a initialized random bits
* @param b initialized random bits
* @param c key to be hashed
* @return randomized c bits (hashed c)
*/
public static int mix(int a, int b, int c) {
a = a - b;
a = a - c;
a = a ^ (c >>> 13);
b = b - c;
b = b - a;
b = b ^ (a << 8);
c = c - a;
c = c - b;
c = c ^ (b >>> 13);
a = a - b;
a = a - c;
a = a ^ (c >>> 12);
b = b - c;
b = b - a;
b = b ^ (a << 16);
c = c - a;
c = c - b;
c = c ^ (b >>> 5);
a = a - b;
a = a - c;
a = a ^ (c >>> 3);
b = b - c;
b = b - a;
b = b ^ (a << 10);
c = c - a;
c = c - b;
c = c ^ (b >>> 15);
return c;
}
/**
* Based on an original suggestion on Robert Jenkin's part in 1997 and Thomas Wang 2007 updates. <p/> <h3>Links</h3>
* <a href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
* <a href="http://en.wikipedia.org/wiki/Jenkins_hash_function's">http://en.wikipedia.org/wiki/Jenkins_hash_function's</a><br/>
*
* @param key key to be hashed
* @return hashed value
*/
public static int JenkinWang32shift(int key) {
// key = (key << 15) - key - 1;
// (~x) + y is equivalent to y - x - 1 in two's complement representation.
key = (~key) + (key << 15);
key = key ^ (key >>> 12);
key = key + (key << 2);
key = key ^ (key >>> 4);
key = key * 2057; // key = (key + (key << 3)) + (key << 11);
key = key ^ (key >>> 16);
return key;
}
/**
* This method uses a combination of bit shifts and integer multiplication to hash the input key. <p/>
* <h3>Links</h3> <a href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
*
* @param key key to be hashed
* @return hashed key
*/
public static int Wang32shiftmult(int key) {
int c2 = 0x27d4eb2d; // a prime or an odd constant
key = (key ^ 61) ^ (key >>> 16);
key = key + (key << 3);
key = key ^ (key >>> 4);
key = key * c2;
key = key ^ (key >>> 15);
return key;
}
/**
* Based on an original suggestion on Robert Jenkin's part in 1997 and Thomas Wang 2007 updates. <p/> <h3>Links</h3>
* <a href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
* <a href="http://en.wikipedia.org/wiki/Jenkins_hash_function's">http://en.wikipedia.org/wiki/Jenkins_hash_function's</a><br/>
*
* @param key key to be hashed
* @return hashed value
*/
public static long JenkinWang64shift(long key) {
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >>> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >>> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >>> 28);
key = key + (key << 31);
return key;
}
/**
* Hashing long to int. <p/> <h3>Links</h3> <a href="http://www.concentric.net/~ttwang/tech/inthash.htm">http://www.concentric.net/~ttwang/tech/inthash.htm</a><br/>
*
* @param key key to be hashed
* @return hashed value
*/
public static int Wang64to32shift(long key) {
key = (~key) + (key << 18); // key = (key << 18) - key - 1;
key = key ^ (key >>> 31);
key = key * 21; // key = (key + (key << 2)) + (key << 4);
key = key ^ (key >>> 11);
key = key + (key << 6);
key = key ^ (key >>> 22);
return (int) key;
}
/**
* Fowler/Noll/Vo hash algorithms FNV_BASIS constant <p/> <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param">http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param</a><br/>
*/
public static final long FNV_BASIS = 0x811c9dc5;
/**
* Fowler/Noll/Vo hash algorithms FNV_PRIME constant for 32 bit hash <p/> <h3>Links</h3> <a
* href="http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param">http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param</a><br/>
*/
public static final long FNV_PRIME_32 = 16777619;//(1 << 24) + 0x193;
/**
* Fowler/Noll/Vo hash algorithms FNV_PRIME constant for 64 bit hash <p/> <h3>Links</h3> <a
* href="http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param">http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param</a><br/>
*/
public static final long FNV_PRIME_64 = 1099511628211L;
/**
* Fowler-Noll-Vo 32 bit hash (FNV-1a) for bytes array.<br/> <p/> <h3>Algorithm</h3> <p/>
* <pre>
* hash = offset_basis
* for each octet_of_data to be hashed
* hash = hash xor octet_of_data
* hash = hash * FNV_prime
* return hash</pre>
*
* <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param bytes bytes array to hash
* @return hash of the initial bytes array
*/
public static int FVN32hash(byte[] bytes) {
long hash = FNV_BASIS;
for (int i = 0; i < bytes.length; i++) {
hash ^= 0xFF & bytes[i];
hash *= FNV_PRIME_32;
}
return (int) hash;
}
/**
* Fowler-Noll-Vo 32 bit hash (FNV-1a) for integer key. This is big-endian version (native endianess of JVM).<br/>
* <p/> <h3>Algorithm</h3> <p/>
* <pre>
* hash = offset_basis
* for each octet_of_data to be hashed
* hash = hash xor octet_of_data
* hash = hash * FNV_prime
* return hash</pre>
*
* <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param c integer key to be hashed
* @return hash 32 bit hash
*/
public static int FVN32hash(int c) {
long hash = FNV_BASIS;
hash ^= c >>> 24;
hash *= FNV_PRIME_32;
hash ^= 0xFF & (c >>> 16);
hash *= FNV_PRIME_32;
hash ^= 0xFF & (c >>> 8);
hash *= FNV_PRIME_32;
hash ^= 0xFF & c;
hash *= FNV_PRIME_32;
return (int) hash;
}
/**
* Fowler-Noll-Vo 64 bit hash (FNV-1a) for bytes array.<br/> <p/> <h3>Algorithm</h3> <p/>
* <pre>
* hash = offset_basis
* for each octet_of_data to be hashed
* hash = hash xor octet_of_data
* hash = hash * FNV_prime
* return hash</pre>
*
* <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param bytes bytes array to hash
* @return hash 64 bit hash
*/
public static long FVN64hash(byte[] bytes) {
long hash = FNV_BASIS;
for (int i = 0; i < bytes.length; i++) {
hash ^= 0xFF & bytes[i];
hash *= FNV_PRIME_64;
}
return hash;
}
/**
* Fowler-Noll-Vo 64 bit hash (FNV-1a) for long key. This is big-endian version (native endianess of JVM).<br/> <p/>
* <h3>Algorithm</h3> <p/>
* <pre>
* hash = offset_basis
* for each octet_of_data to be hashed
* hash = hash xor octet_of_data
* hash = hash * FNV_prime
* return hash</pre>
*
* <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param c long key to be hashed
* @return hash 64 bit hash
*/
public static long FVN64hash(long c) {
long hash = FNV_BASIS;
hash ^= c >>> 56;
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 48);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 40);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 32);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 24);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 16);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & (c >>> 8);
hash *= FNV_PRIME_64;
hash ^= 0xFFL & c;
hash *= FNV_PRIME_64;
return hash;
}
/**
* Fowler-Noll-Vo 32 bit hash (FNV-1a) for long key. This is big-endian version (native endianess of JVM).<br/> <p/>
* <h3>Algorithm</h3> <p/>
* <pre>
* hash = offset_basis
* for each octet_of_data to be hashed
* hash = hash xor octet_of_data
* hash = hash * FNV_prime
* return hash</pre>
*
* <h3>Links</h3> <a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a><br/>
* <a href="http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a><br/>
*
* @param c long key to be hashed
* @return hash 32 bit hash
*/
public static int FVN64to32hash(long c) {
long hash = FNV_BASIS;
hash ^= c >>> 56;
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 48);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 40);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 32);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 24);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 16);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & (c >>> 8);
hash *= FNV_PRIME_32;
hash ^= 0xFFL & c;
hash *= FNV_PRIME_32;
return (int) hash;
}
/**
* MurmurHash hash function for bytes array. <p/> <h3>Links</h3> <a href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a><br/>
* <a href="http://dmy999.com/article/50/murmurhash-2-java-port">http://dmy999.com/article/50/murmurhash-2-java-port</a><br/>
* <a href="http://en.wikipedia.org/wiki/MurmurHash">http://en.wikipedia.org/wiki/MurmurHash</a><br/>
*
* @param data bytes to be hashed
* @param seed seed parameter
* @return 32 bit hash
*/
@SuppressWarnings("fallthrough")
public static int MurmurHash2(byte[] data, int seed) {
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
int m = 0x5bd1e995;
int r = 24;
// Initialize the hash to a 'random' value
int len = data.length;
int h = seed ^ len;
int i = 0;
while (len >= 4) {
int k = data[i] & 0xFF;
k |= (data[i + 1] & 0xFF) << 8;
k |= (data[i + 2] & 0xFF) << 16;
k |= (data[i + 3] & 0xFF) << 24;
k *= m;
k ^= k >>> r;
k *= m;
h *= m;
h ^= k;
i += 4;
len -= 4;
}
switch (len) {
case 3:
h ^= (data[i + 2] & 0xFF) << 16;
case 2:
h ^= (data[i + 1] & 0xFF) << 8;
case 1:
h ^= (data[i] & 0xFF);
h *= m;
}
h ^= h >>> 13;
h *= m;
h ^= h >>> 15;
return h;
}
/**
* MurmurHash hash function integer. <p/> <h3>Links</h3> <a href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a><br/>
* <a href="http://dmy999.com/article/50/murmurhash-2-java-port">http://dmy999.com/article/50/murmurhash-2-java-port</a><br/>
* <a href="http://en.wikipedia.org/wiki/MurmurHash">http://en.wikipedia.org/wiki/MurmurHash</a><br/>
*
* @param c int to be hashed
* @param seed seed parameter
* @return 32 bit hash
*/
public static int MurmurHash2(int c, int seed) {
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
int m = 0x5bd1e995;
// Initialize the hash to a 'random' value
int h = seed ^ 4;
c *= m;
c ^= c >>> 24;
c *= m;
h *= m;
h ^= c;
h ^= h >>> 13;
h *= m;
h ^= h >>> 15;
return h;
}
/**
* MurmurHash hash function for bytes array with default seed value equals 0x2f1a32b3. <p/> <h3>Links</h3> <a
* href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a><br/> <a
* href="http://dmy999.com/article/50/murmurhash-2-java-port">http://dmy999.com/article/50/murmurhash-2-java-port</a><br/>
* <a href="http://en.wikipedia.org/wiki/MurmurHash">http://en.wikipedia.org/wiki/MurmurHash</a><br/>
*
* @param data bytes to be hashed
* @return 32 bit hash
*/
public static int MurmurHash2(byte[] data) {
return MurmurHash2(data, 0x2f1a32b3);
}
/**
* MurmurHash hash function integer with default seed value equals to 0x2f1a32b3. <p/> <h3>Links</h3> <a
* href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a><br/> <a
* href="http://dmy999.com/article/50/murmurhash-2-java-port">http://dmy999.com/article/50/murmurhash-2-java-port</a><br/>
* <a href="http://en.wikipedia.org/wiki/MurmurHash">http://en.wikipedia.org/wiki/MurmurHash</a><br/>
*
* @param c int to be hashed\
* @return 32 bit hash
*/
public static int MurmurHash2(int c) {
return MurmurHash2(c, 0x2f1a32b3);
}
}