package squidpony.squidmath;
import squidpony.annotation.Beta;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Simple hashing functions that we can rely on staying the same cross-platform.
* The static methods of this class (not its inner classes) use the Fowler/Noll/Vo
* Hash (FNV-1a) algorithm, which is public domain. The hashes this returns are always
* 0 when given null to hash. Arrays with identical elements of identical types will
* hash identically. Arrays with identical numerical values but different types will
* hash differently (only for FNV-1a, not the inner classes). FNV-1a may be somewhat
* slow if you are running many hashes or hashing very large data; in that case you
* should consider one of the inner classes, Lightning or Storm.
* <br>
* There are several static inner classes in CrossHash, Lightning, Storm, Falcon, Chariot, Wisp,
* and Mist, that provide different hashing properties, as well as the inner IHasher interface.
* Of these, Wisp should be the default choice in most cases. If you need a salt to alter the
* hash function, using one of a large family of such functions instead of a single function like
* Wisp, then Storm and Mist are good choices (Mist is faster, Storm is more stable API-wise).
* Falcon and Lightning are mostly superseded by Wisp. Chariot has some limitations, and uses
* potentially much more memory than the others (which use almost none).
* <br>
* IHasher values are provided as static fields, and use Wisp to hash a specific type or fall
* back to Object.hashCode if given an object with the wrong type. IHasher values are optional
* parts of OrderedMap and OrderedSet, and allow arrays to be used as keys in those collections
* while keeping hashing by value instead of the normal hashing by reference for arrays. You
* probably won't ever need to make a class that implements IHasher yourself; for some cases you
* may want to look at the {@link Hashers} class for additional functions.
* <br>
* The inner classes provide alternate, faster hashing algorithms. Lightning, Wisp, and Falcon
* have no theoretical basis or grounds in any reason other than empirical testing for why they
* do what they do, and this seems to be in line with many widely-used hashes (see: The Art of
* Hashing, http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx ). That said, Wisp
* performs very well, ahead of Arrays.hashCode (10.5 ms instead of 15 ms) for over a million
* hashes of 16-element long arrays, not including overhead for generating them, while SipHash and
* FNV-1a take approximately 80 ms and 135-155 ms, respectively, for the same data). Lightning and
* Falcon perform less-well, with Lightning taking 17 ms instead of 15 ms for Arrays.hashCode, and
* Falcon taking about 12.3 ms but slowing down somewhat if a 32-bit hash is requested from long
* data. All of these have good, low, collision rates on Strings and long arrays.
* <br>
* Storm is a variant on Lightning with 64 bits for a salt-like modifier as a member variable,
* which can make 2 to the 64 individual hashing functions from one set of code. Mist is similar,
* but has 128 bits for some hashes (including any calls to hash64()) and 64 bits for some other
* hashes (only calls to hash() with data that doesn't involve long or double arrays). Storm and Mist
* have some properties of a cryptographic hash, but is not recommended it for that usage. It is,
* however ideal for situations that show up often in game development where end users may be able
* to see and possibly alter some information that you don't want changed (i.e. save data stored on
* a device or in the browser's LocalStorage). If you want a way to verify the data is what you
* think it is, you can store a hash, using one of the many-possible hash functions this can
* produce, somewhere else and verify that the saved data has the hash it did last time; if the
* exact hashing function isn't known (or exact functions aren't known) by a tampering user,
* then it is unlikely they can make the hash match even if they can edit it. Storm is slightly
* slower than Lightning just like Mist is slightly slower than Wisp, at about 20 ms for a
* million hashes with Storm instead of Lightning's 17 ms, and at about 18 ms for Mist for the same
* data instead of Wisp's 10.5, but should never be worse than twice as slow as Arrays.hashCode, and
* is still about three times faster than the similar SipHash that SquidLib previously had here.
* <br>
* All of the hashes used here have about the same rate of collisions on identical data
* (testing used Arrays.hashCode, all the hashes in here now, and the now-removed SipHash), with
* any fluctuation within some small margin of error. Lightning has been changed frequently but
* is considered stable now, but it isn't being considered to replace the FNV-1a algorithm in
* CrossHash for compatibility reasons. Both Lightning and Wisp seem to meet all the criteria for
* a good hash function, though, including doing well with a visual test that shows issues in
* FNV-1a and especially Arrays.hashCode. Wisp is considered stable now, and is not considered yet
* for replacing FNV-1a for the same reason. Chariot is not recommended. Falcon is only recommended
* if you need an alternate algorithm for some reason and need only 64-bit hashes; it has issues when
* generating 32-bit hashes of long arrays or double arrays. Mist is not considered stable yet, but
* probably will be soon; it offers an improvement on speed and salting over Storm, and its collision
* rates were very very low in testing.
* <br>
* To help find patterns in hash output in a visual way, you can hash an x,y point, take the bottom 24 bits,
* and use that as an RGB color for the pixel at that x,y point. On a 512x512 grid of points, the patterns
* in Arrays.hashCode and the default CrossHash algorithm (FNV-1a) are evident, and Sip (implementing SipHash)
* does approximately as well as Lightning, with no clear patterns visible (Sip has been removed from SquidLib
* because it needs a lot of code and is slower than Storm and especially Lightning). The idea is from a
* technical report on visual uses for hashing, http://www.clockandflame.com/media/Goulburn06.pdf .
* <ul>
* <li>{@link java.util.Arrays#hashCode(int[])}: http://i.imgur.com/S4Gh1sX.png</li>
* <li>{@link CrossHash#hash(int[])}: http://i.imgur.com/x8SDqvL.png</li>
* <li>(Former) CrossHash.Sip.hash(int[]): http://i.imgur.com/keSpIwm.png</li>
* <li>{@link CrossHash.Lightning#hash(int[])}: http://i.imgur.com/afGJ9cA.png</li>
* </ul>
* <br>
* Note: This class was formerly called StableHash, but since that refers to a specific
* category of hashing algorithm that this is not, and since the goal is to be cross-
* platform, the name was changed to CrossHash.
* Created by Tommy Ettinger on 1/16/2016.
*
* @author Glenn Fowler
* @author Phong Vo
* @author Landon Curt Noll
* @author Tommy Ettinger
*/
public class CrossHash {
public static int hash(final boolean[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length - 1, o = 0;
for (int i = 0; i <= len; i++) {
o |= (data[i]) ? (1 << (i & 7)) : 0;
if ((i & 7) == 7 || i == len) {
h ^= o;
h *= 16777619;
o = 0;
}
}
return h;
}
public static int hash(final byte[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i];
h *= 16777619;
}
return h;
}
public static int hash(final char[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 16777619;
h ^= data[i] >>> 8;
h *= 16777619;
}
return h;
}
public static int hash(final short[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 16777619;
h ^= data[i] >>> 8;
h *= 16777619;
}
return h;
}
public static int hash(final int[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 16777619;
h ^= (data[i] >>> 8) & 0xff;
h *= 16777619;
h ^= (data[i] >>> 16) & 0xff;
h *= 16777619;
h ^= data[i] >>> 24;
h *= 16777619;
}
return h;
}
public static int hash(final long[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
for (int i = 0; i < len; i++) {
h ^= (int) (data[i] & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 8) & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 16) & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 24) & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 32) & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 40) & 0xff);
h *= 16777619;
h ^= (int) ((data[i] >>> 48) & 0xff);
h *= 16777619;
h ^= (int) (data[i] >>> 56);
h *= 16777619;
}
return h;
}
public static int hash(final float[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length, t;
for (int i = 0; i < len; i++) {
t = NumberTools.floatToIntBits(data[i]);
h ^= t & 0xff;
h *= 16777619;
h ^= (t >>> 8) & 0xff;
h *= 16777619;
h ^= (t >>> 16) & 0xff;
h *= 16777619;
h ^= t >>> 24;
h *= 16777619;
}
return h;
}
public static int hash(final double[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
long t;
for (int i = 0; i < len; i++) {
t = NumberTools.doubleToLongBits(data[i]);
h ^= (int) (t & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 8) & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 16) & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 24) & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 32) & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 40) & 0xff);
h *= 16777619;
h ^= (int) ((t >>> 48) & 0xff);
h *= 16777619;
h ^= (int) (t >>> 56);
h *= 16777619;
}
return h;
}
public static int hash(final CharSequence data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length();
for (int i = 0; i < len; i++) {
h ^= data.charAt(i) & 0xff;
h *= 16777619;
h ^= data.charAt(i) >>> 8;
h *= 16777619;
}
return h;
}
public static int hash(final CharSequence[] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash(data[i]);
h ^= t & 0xff;
h *= 16777619;
h ^= (t >>> 8) & 0xff;
h *= 16777619;
h ^= (t >>> 16) & 0xff;
h *= 16777619;
h ^= t >>> 24;
h *= 16777619;
}
return h;
}
public static int hash(final char[][] data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash(data[i]);
h ^= t & 0xff;
h *= 16777619;
h ^= (t >>> 8) & 0xff;
h *= 16777619;
h ^= (t >>> 16) & 0xff;
h *= 16777619;
h ^= t >>> 24;
h *= 16777619;
}
return h;
}
public static int hash(final CharSequence[]... data) {
if (data == null)
return 0;
int h = -2128831035, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash(data[i]);
h ^= t & 0xff;
h *= 16777619;
h ^= (t >>> 8) & 0xff;
h *= 16777619;
h ^= (t >>> 16) & 0xff;
h *= 16777619;
h ^= t >>> 24;
h *= 16777619;
}
return h;
}
/**
* Hashes only a subsection of the given data, starting at start (inclusive) and ending before end (exclusive).
*
* @param data the char array to hash
* @param start the start of the section to hash (inclusive)
* @param end the end of the section to hash (exclusive)
* @return
*/
public static int hash(final char[] data, int start, int end) {
if (data == null)
return 0;
int h = -2128831035, len = data.length;
start %= len;
end %= len;
if (end <= start || start < 0 || end <= 0)
return 0;
for (int i = start; i < end; i++) {
h ^= data[i] & 0xff;
h *= 16777619;
h ^= data[i] >>> 8;
h *= 16777619;
}
return h;
}
public static long hash64(final boolean[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length - 1, o = 0;
for (int i = 0; i <= len; i++) {
o |= (data[i]) ? (1 << (i & 7)) : 0;
if ((i & 7) == 7 || i == len) {
h ^= o;
h *= 1099511628211L;
o = 0;
}
}
return h;
}
public static long hash64(final byte[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i];
h *= 1099511628211L;
}
return h;
}
public static long hash64(final char[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 1099511628211L;
h ^= data[i] >>> 8;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final short[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 1099511628211L;
h ^= data[i] >>> 8;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final int[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 16) & 0xff;
h *= 1099511628211L;
h ^= data[i] >>> 24;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final long[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
for (int i = 0; i < len; i++) {
h ^= data[i] & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (data[i] >>> 48) & 0xff;
h *= 1099511628211L;
h ^= data[i] >>> 56;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final float[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length, t;
for (int i = 0; i < len; i++) {
t = NumberTools.floatToIntBits(data[i]);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= t >>> 24;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final double[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length, t;
for (int i = 0; i < len; i++) {
t = NumberTools.doubleToLongBits(data[i]);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 48) & 0xff;
h *= 1099511628211L;
h ^= t >>> 56;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final CharSequence data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length();
for (int i = 0; i < len; i++) {
h ^= data.charAt(i) & 0xff;
h *= 1099511628211L;
h ^= data.charAt(i) >>> 8;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final CharSequence[] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash64(data[i]);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 48) & 0xff;
h *= 1099511628211L;
h ^= t >>> 56;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final char[][] data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash64(data[i]);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 48) & 0xff;
h *= 1099511628211L;
h ^= t >>> 56;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
long h = -3750763034362895579L, t;
for (CharSequence datum : data) {
t = hash64(datum);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 48) & 0xff;
h *= 1099511628211L;
h ^= t >>> 56;
h *= 1099511628211L;
}
return h;
}
public static long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length, t;
for (int i = 0; i < len; i++) {
t = hash64(data[i]);
h ^= t & 0xff;
h *= 1099511628211L;
h ^= (t >>> 8) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 16) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 24) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 32) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 40) & 0xff;
h *= 1099511628211L;
h ^= (t >>> 48) & 0xff;
h *= 1099511628211L;
h ^= t >>> 56;
h *= 1099511628211L;
}
return h;
}
/**
* Hashes only a subsection of the given data, starting at start (inclusive) and ending before end (exclusive).
*
* @param data the char array to hash
* @param start the start of the section to hash (inclusive)
* @param end the end of the section to hash (exclusive)
* @return
*/
public static long hash64(final char[] data, int start, int end) {
if (data == null)
return 0;
long h = -3750763034362895579L, len = data.length;
start %= len;
end %= len;
if (end <= start || start < 0 || end <= 0)
return 0;
for (int i = start; i < end; i++) {
h ^= data[i] & 0xff;
h *= 1099511628211L;
h ^= data[i] >>> 8;
h *= 1099511628211L;
}
return h;
}
/**
* An interface that can be used to move the logic for the hashCode() and equals() methods from a class' methods to
* an implementation of IHasher that certain collections in SquidLib can use. Primarily useful when the key type is
* an array, which normally doesn't work as expected in Java hash-based collections, but can if the right collection
* and IHasher are used. See also {@link Hashers} for additional implementations, some of which need dependencies on
* things the rest of CrossHash doesn't, like a case-insensitive String hasher/equator that uses RegExodus to handle
* CharSequence comparison on GWT.
*/
public interface IHasher extends Serializable {
/**
* If data is a type that this IHasher can specifically hash, this method should use that specific hash; in
* other situations, it should simply delegate to calling {@link Object#hashCode()} on data. The body of an
* implementation of this method can be very small; for an IHasher that is meant for byte arrays, the body could
* be: {@code return (data instanceof byte[]) ? CrossHash.Lightning.hash((byte[]) data) : data.hashCode();}
*
* @param data the Object to hash; this method should take any type but often has special behavior for one type
* @return a 32-bit int hash code of data
*/
int hash(final Object data);
/**
* Not all types you might want to use an IHasher on meaningfully implement .equals(), such as array types; in
* these situations the areEqual method helps quickly check for equality by potentially having special logic for
* the type this is meant to check. The body of implementations for this method can be fairly small; for byte
* arrays, it looks like: {@code return left == right
* || ((left instanceof byte[] && right instanceof byte[])
* ? Arrays.equals((byte[]) left, (byte[]) right)
* : Objects.equals(left, right));} , but for multidimensional arrays you should use the
* {@link #equalityHelper(Object[], Object[], IHasher)} method with an IHasher for the inner arrays that are 1D
* or otherwise already-hash-able, as can be seen in the body of the implementation for 2D char arrays, where
* charHasher is an existing IHasher that handles 1D arrays:
* {@code return left == right
* || ((left instanceof char[][] && right instanceof char[][])
* ? equalityHelper((char[][]) left, (char[][]) right, charHasher)
* : Objects.equals(left, right));}
*
* @param left allowed to be null; most implementations will have special behavior for one type
* @param right allowed to be null; most implementations will have special behavior for one type
* @return true if left is equal to right (preferably by value, but reference equality may sometimes be needed)
*/
boolean areEqual(final Object left, final Object right);
}
/**
* Not a general-purpose method; meant to ease implementation of {@link IHasher#areEqual(Object, Object)}
* methods when the type being compared is a multi-dimensional array (which normally requires the heavyweight method
* {@link Arrays#deepEquals(Object[], Object[])} or doing more work yourself; this reduces the work needed to
* implement fixed-depth equality). As mentioned in the docs for {@link IHasher#areEqual(Object, Object)}, example
* code that hashes 2D char arrays can be done using an IHasher for 1D char arrays called charHasher:
* {@code return left == right
* || ((left instanceof char[][] && right instanceof char[][])
* ? equalityHelper((char[][]) left, (char[][]) right, charHasher)
* : Objects.equals(left, right));}
*
* @param left
* @param right
* @param inner
* @return
*/
public static boolean equalityHelper(Object[] left, Object[] right, IHasher inner) {
if (left == right)
return true;
if ((left == null) ^ (right == null))
return false;
for (int i = 0; i < left.length && i < right.length; i++) {
if (!inner.areEqual(left[i], right[i]))
return false;
}
return true;
}
private static class BooleanHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
BooleanHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof boolean[]) ? CrossHash.Wisp.hash((boolean[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof boolean[] && right instanceof boolean[]) ? Arrays.equals((boolean[]) left, (boolean[]) right) : Objects.equals(left, right));
}
}
public static final IHasher booleanHasher = new BooleanHasher();
private static class ByteHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
ByteHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof byte[]) ? CrossHash.Wisp.hash((byte[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right
|| ((left instanceof byte[] && right instanceof byte[])
? Arrays.equals((byte[]) left, (byte[]) right)
: Objects.equals(left, right));
}
}
public static final IHasher byteHasher = new ByteHasher();
private static class ShortHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
ShortHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof short[]) ? CrossHash.Wisp.hash((short[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof short[] && right instanceof short[]) ? Arrays.equals((short[]) left, (short[]) right) : Objects.equals(left, right));
}
}
public static final IHasher shortHasher = new ShortHasher();
private static class CharHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
CharHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof char[]) ? CrossHash.Wisp.hash((char[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof char[] && right instanceof char[]) ? Arrays.equals((char[]) left, (char[]) right) : Objects.equals(left, right));
}
}
public static final IHasher charHasher = new CharHasher();
private static class IntHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
IntHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof int[]) ? CrossHash.Wisp.hash((int[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return (left instanceof int[] && right instanceof int[]) ? Arrays.equals((int[]) left, (int[]) right) : Objects.equals(left, right);
}
}
public static final IHasher intHasher = new IntHasher();
private static class LongHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
LongHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof long[]) ? CrossHash.Wisp.hash((long[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return (left instanceof long[] && right instanceof long[]) ? Arrays.equals((long[]) left, (long[]) right) : Objects.equals(left, right);
}
}
public static final IHasher longHasher = new LongHasher();
private static class FloatHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
FloatHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof float[]) ? CrossHash.Wisp.hash((float[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof float[] && right instanceof float[]) ? Arrays.equals((float[]) left, (float[]) right) : Objects.equals(left, right));
}
}
public static final IHasher floatHasher = new FloatHasher();
private static class DoubleHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
DoubleHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof double[]) ? CrossHash.Wisp.hash((double[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof double[] && right instanceof double[]) ? Arrays.equals((double[]) left, (double[]) right) : Objects.equals(left, right));
}
}
public static final IHasher doubleHasher = new DoubleHasher();
private static class Char2DHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
Char2DHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof char[][]) ? CrossHash.Wisp.hash((char[][]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right
|| ((left instanceof char[][] && right instanceof char[][])
? equalityHelper((char[][]) left, (char[][]) right, charHasher)
: Objects.equals(left, right));
}
}
public static final IHasher char2DHasher = new Char2DHasher();
private static class Int2DHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
Int2DHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof int[][]) ? CrossHash.Wisp.hash((int[][]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right
|| ((left instanceof int[][] && right instanceof int[][])
? equalityHelper((int[][]) left, (int[][]) right, intHasher)
: Objects.equals(left, right));
}
}
public static final IHasher int2DHasher = new Int2DHasher();
private static class Long2DHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
Long2DHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof long[][]) ? CrossHash.Wisp.hash((long[][]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right
|| ((left instanceof long[][] && right instanceof long[][])
? equalityHelper((long[][]) left, (long[][]) right, longHasher)
: Objects.equals(left, right));
}
}
public static final IHasher long2DHasher = new Long2DHasher();
private static class StringHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
StringHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof CharSequence) ? CrossHash.Wisp.hash((CharSequence) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return Objects.equals(left, right);
}
}
public static final IHasher stringHasher = new StringHasher();
private static class StringArrayHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
StringArrayHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof CharSequence[]) ? CrossHash.Wisp.hash((CharSequence[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof CharSequence[] && right instanceof CharSequence[]) ? equalityHelper((CharSequence[]) left, (CharSequence[]) right, stringHasher) : Objects.equals(left, right));
}
}
/**
* Though the name suggests this only hashes String arrays, it can actually hash any CharSequence array as well.
*/
public static final IHasher stringArrayHasher = new StringArrayHasher();
private static class ObjectArrayHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
ObjectArrayHasher() {
}
@Override
public int hash(final Object data) {
return (data instanceof Object[]) ? CrossHash.Wisp.hash((Object[]) data) : data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right || ((left instanceof Object[] && right instanceof Object[]) && Arrays.equals((Object[]) left, (Object[]) right) || Objects.equals(left, right));
}
}
public static final IHasher objectArrayHasher = new ObjectArrayHasher();
private static class DefaultHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
DefaultHasher() {
}
@Override
public int hash(final Object data) {
return data.hashCode();
}
@Override
public boolean areEqual(Object left, Object right) {
return Objects.equals(left, right);
}
}
public static final IHasher defaultHasher = new DefaultHasher();
private static class IdentityHasher implements IHasher, Serializable
{
private static final long serialVersionUID = 3L;
IdentityHasher() { }
@Override
public int hash(Object data) {
return System.identityHashCode(data);
}
@Override
public boolean areEqual(Object left, Object right) {
return left == right;
}
}
public static final IHasher identityHasher = new IdentityHasher();
private static class GeneralHasher implements IHasher, Serializable {
private static final long serialVersionUID = 3L;
GeneralHasher() {
}
@Override
public int hash(final Object data) {
return CrossHash.Wisp.hash(data);
}
@Override
public boolean areEqual(Object left, Object right) {
if(left == right) return true;
Class l = left.getClass(), r = right.getClass();
if(l == r)
{
if(l.isArray())
{
if(left instanceof int[]) return Arrays.equals((int[]) left, (int[]) right);
else if(left instanceof long[]) return Arrays.equals((long[]) left, (long[]) right);
else if(left instanceof char[]) return Arrays.equals((char[]) left, (char[]) right);
else if(left instanceof double[]) return Arrays.equals((double[]) left, (double[]) right);
else if(left instanceof boolean[]) return Arrays.equals((boolean[]) left, (boolean[]) right);
else if(left instanceof byte[]) return Arrays.equals((byte[]) left, (byte[]) right);
else if(left instanceof float[]) return Arrays.equals((float[]) left, (float[]) right);
else if(left instanceof short[]) return Arrays.equals((short[]) left, (short[]) right);
else if(left instanceof char[][]) return equalityHelper((char[][]) left, (char[][]) right, charHasher);
else if(left instanceof int[][]) return equalityHelper((int[][]) left, (int[][]) right, intHasher);
else if(left instanceof long[][]) return equalityHelper((long[][]) left, (long[][]) right, longHasher);
else if(left instanceof CharSequence[]) return equalityHelper((CharSequence[]) left, (CharSequence[]) right, stringHasher);
else if(left instanceof Object[]) return Arrays.equals((Object[]) left, (Object[]) right);
}
return Objects.equals(left, right);
}
return false;
}
}
/**
* This IHasher is the one you should use if you aren't totally certain what types will go in an OrderedMap's keys
* or an OrderedSet's items, since it can handle mixes of elements.
*/
public static final IHasher generalHasher = new GeneralHasher();
/**
* A quick, simple hashing function that seems to have good results. Like LightRNG, it stores a state that
* it updates independently of the output, and this starts at a large prime. At each step, it takes the
* current item in the array being hashed, adds a large non-prime used in LightRNG's generation function
* (it's 2 to the 64, times the golden ratio phi, and truncated to a signed long), multiplies by a prime
* called the "state multiplier", adds the result to the state and stores it, multiplies the value of the
* state by another prime called the "output multiplier", then XORs the current result with that value
* before moving onto the next item in the array. A finalization step XORs the result with a complex value
* made by adding the state (left over from the previous step) to what was the output multiplier, adding
* the last known value for result to the phi-related constant from LightRNG, multiplying that pair, adding
* the initial state (which turns out to be unusually good for this, despite no particularly special numeric
* qualities other than being a probable prime) and then bitwise-rotating it left by a seemingly-random
* number drawn from the highest 6 bits of the state.
* <br>
* This all can be done very quickly; a million hashes of a million different 16-element long arrays can be
* computed in under 18-20 ms (in the benchmark, some amount of that is overhead from generating a new
* array with LongPeriodRNG, since the benchmark uses that RNG's state for data, and the default
* Arrays.hashCode implementation is only somewhat faster at under 16 ms). After several tries and tweaks
* to the constants this uses, it also gets remarkably few hash collisions. On the same 0x100000, or
* 1048576, RNG states for data, Lightning gets 110 collisions, the JDK Arrays.hashCode method gets 129
* collisions, Sip (implementing SipHash) gets 145 collisions, and CrossHash (using the FNV-1a algorithm)
* gets 135 collisions. Storm depends on the salt chosen, but with one initialized with the phi-based
* constant that shows up in LightRNG and here, Storm gets 116 collisions. Dispersion is not perfect, but
* at many bit sizes Lightning continues to have less collisions (it disperses better than the other hashes
* with several quantities of bits, at least on this test data). Lightning also does relatively well, though
* it isn't clearly ahead of the rest all the time, when hashing Strings, especially ones that use a larger
* set of letters, it seems (FakeLanguageGen was used to make test data, and languages that used more
* characters in their alphabet seemed to hash better with this than competing hashes for some reason).
* <br>
* There is certainly room for improvement with the specific numbers chosen; earlier versions used the
* state multiplier "Neely's number", which is a probable prime made by taking the radix-29 number
* "HARGNALLINSCLOPIOPEEPIO" (a reference to a very unusual TV show), truncating to 64 bits, and rotating
* right by 42 bits. This version uses "Neely's number" for an initial state and during finalization, and
* uses a different probable prime as the state multiplier, made with a similar process; it starts with the
* radix-36 number "EDSGERWDIJKSTRA", then does the same process but rotates right by 37 bits to obtain a
* different prime. This tweak seems to help with hash collisions. Extensive trial and error was used to
* find the current output multiplier, which has no real relationship to anything else but has exactly 32 of
* 64 bits set to 1, has 1 in the least and most significant bit indices (meaning it is negative and odd),
* and other than that seems to have better results on most inputs for mystifying reasons. Earlier versions
* applied a Gray code step to alter the output instead of a multiplier that heavily overflows to obfuscate
* state, but that had a predictable pattern for most of the inputs tried, which seemed less-than-ideal for
* a hash. Vitally, Lightning avoids predictable collisions that Arrays.hashCode has, like
* {@code Arrays.hashCode(new long[]{0})==Arrays.hashCode(new long[]{-1})}.
* <br>
* The output multiplier is 0xC6BC279692B5CC83L, the state multiplier is 0xD0E89D2D311E289FL, the number
* added to the state (from LightRNG and code derived from FastUtil, but obtained from the golden ratio
* phi) is 0x9E3779B97F4A7C15L, and the starting state ("Neely's Number") is 0x632BE59BD9B4E019L.
* <br>
* To help find patterns in hash output in a visual way, you can hash an x,y point, take the bottom 24 bits,
* and use that as an RGB color for the pixel at that x,y point. On a 512x512 grid of points, the patterns
* in Arrays.hashCode and the default CrossHash algorithm (FNV-1a) are evident, and Sip (implementing
* SipHash) does approximately as well as Lightning, with no clear patterns visible (Sip has been removed
* from SquidLib because it needs a lot of code and is slower than Storm and especially Lightning). The
* idea is from a technical report on visual uses for hashing,
* http://www.clockandflame.com/media/Goulburn06.pdf .
* <ul>
* <li>{@link java.util.Arrays#hashCode(int[])}: http://i.imgur.com/S4Gh1sX.png</li>
* <li>{@link CrossHash#hash(int[])}: http://i.imgur.com/x8SDqvL.png</li>
* <li>(Former) CrossHash.Sip.hash(int[]): http://i.imgur.com/keSpIwm.png</li>
* <li>{@link CrossHash.Lightning#hash(int[])}: http://i.imgur.com/afGJ9cA.png</li>
* </ul>
*/
// tested output multipliers
// 0x DA1A459BD9B4C619L
// 0x DC1A459879B5C619L
// 0x DC1A479829B5E639L
// 0x DC1A479C29B5C639L
// 0x EA1C479692B5C639L
// 0x CA1C479692B5C635L // this gets 105 collisions, low
// 0x CABC479692B5C635L
// 0x DC1A479C29B5C647L
// 0x DC1A479C29B5C725L
// 0x CABC279692B5CB21L
// 0x C6BC279692B5CC83L // this gets 100 collisions, lowest
// 0x C6BC279692B4D8A5L
// 0x C6BC279692B4D345L
// 0x C6EC273692B4A4B9L
// 0x C6A3256B52D5B463L
// 0x C6A3256B52D5B463L
// 0x C6A3256D52D5B4C9L
// 0x D8A3256D52D5B619L
// 0x D96E6AC724658947L
// 0x D96E6AC724658C2DL
// 0x CCABF9E32FD684F9L
// 0x C314163FAF912A01L
// 0x C3246007A332C12AL
// 0x CA1C479692B5C6ABL
// 0x C6B5275692B5CC83 // untested so far
@SuppressWarnings("NumericOverflow")
public static final class Lightning {
public static long hash64(final boolean[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] ? 0x9E3779B97F4A7C94L : 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final byte[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final short[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final char[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final int[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final long[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final float[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (NumberTools.floatToIntBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final double[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (NumberTools.doubleToLongBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i += step) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final CharSequence data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length(); i++) {
result ^= (z += (data.charAt(i) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final char[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final long[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final CharSequence[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (CharSequence datum : data) {
result ^= (z += (hash64(datum) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static long hash64(final Object[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
Object o;
for (int i = 0; i < data.length; i++) {
o = data[i];
result ^= (z += ((o == null ? 0 : o.hashCode()) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public static int hash(final boolean[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] ? 0x9E3779B97F4A7C94L : 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final byte[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final short[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final char[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final int[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final long[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final float[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (NumberTools.floatToIntBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final double[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (NumberTools.doubleToLongBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i += step) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final CharSequence data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length(); i++) {
result ^= (z += (data.charAt(i) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final char[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final long[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final CharSequence[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (CharSequence datum : data) {
result ^= (z += (hash64(datum) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final CharSequence[]... data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public static int hash(final Object[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
Object o;
for (int i = 0; i < data.length; i++) {
o = data[i];
result ^= (z += ((o == null ? 0 : o.hashCode()) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * 0xC6BC279692B5CC83L;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
}
/**
* A whole cluster of Lightning-like hash functions that sacrifice a small degree of speed, but can be
* constructed with a salt value that helps obscure what hashing function is actually being used.
* <br>
* The salt field is not serialized, so it is important that the same salt will be given by the
* program when the same hash results are wanted for some inputs.
* <br>
* A group of 24 static, final, pre-initialized Storm members are present in this class, each with the
* name of a letter in the Greek alphabet (this uses the convention on Wikipedia,
* https://en.wikipedia.org/wiki/Greek_alphabet#Letters , where lambda is spelled with a 'b'). The whole
* group of 24 pre-initialized members are also present in a static array called {@code predefined}.
* These can be useful when, for example, you want to get multiple hashes of a single array or String
* as part of cuckoo hashing or similar techniques that need multiple hashes for the same inputs.
*/
public static final class Storm implements Serializable {
private static final long serialVersionUID = 3152426757973945155L;
private transient long $alt;
public Storm() {
this(0L);
}
public Storm(final CharSequence alteration) {
this(Lightning.hash64(alteration));
}
public Storm(final long alteration) {
long s = alteration + 0x9E3779B97F4A7C15L;
s = (s ^ (s >>> 30)) * 0xBF58476D1CE4E5B9L;
s = (s ^ (s >>> 27)) * 0x94D049BB133111EBL;
s ^= s >>> 31;
$alt = s += 191 - Long.bitCount(s);
}
public static final Storm alpha = new Storm("alpha"), beta = new Storm("beta"), gamma = new Storm("gamma"),
delta = new Storm("delta"), epsilon = new Storm("epsilon"), zeta = new Storm("zeta"),
eta = new Storm("eta"), theta = new Storm("theta"), iota = new Storm("iota"),
kappa = new Storm("kappa"), lambda = new Storm("lambda"), mu = new Storm("mu"),
nu = new Storm("nu"), xi = new Storm("xi"), omicron = new Storm("omicron"), pi = new Storm("pi"),
rho = new Storm("rho"), sigma = new Storm("sigma"), tau = new Storm("tau"),
upsilon = new Storm("upsilon"), phi = new Storm("phi"), chi = new Storm("chi"), psi = new Storm("psi"),
omega = new Storm("omega");
public static final Storm[] predefined = new Storm[]{alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota,
kappa, lambda, mu, nu, xi, omicron, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega};
public long hash64(final boolean[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] ? 0x9E3779B97F4A7C15L : 0x789ABCDEFEDCBA98L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final byte[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final short[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final char[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final int[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final long[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final float[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (NumberTools.floatToIntBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final double[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (NumberTools.doubleToLongBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = start; i < end && i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = start; i < end && i < len; i += step) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final CharSequence data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length();
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data.charAt(i) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58));
}
public long hash64(final char[][] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final long[][] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final CharSequence[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (CharSequence datum : data) {
result ^= (z += (hash64(datum) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public long hash64(final Object[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
Object o;
for (int i = 0; i < len; i++) {
o = data[i];
result ^= (z += ((o == null ? 0 : o.hashCode()) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return result ^ Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58));
}
public int hash(final boolean[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] ? 0x9E3779B97F4A7C15L : 0x789ABCDEFEDCBA98L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final byte[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final short[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final char[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final int[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final long[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final float[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (NumberTools.floatToIntBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final double[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (NumberTools.doubleToLongBits(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = start; i < end && i < len; i++) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = start; i < end && i < len; i += step) {
result ^= (z += (data[i] + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final CharSequence data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length();
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (data.charAt(i) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (z >>> 58))) ^ (result >>> 32));
}
public int hash(final char[][] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final long[][] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final CharSequence[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (CharSequence datum : data) {
result ^= (z += (hash64(datum) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final CharSequence[]... data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
for (int i = 0; i < len; i++) {
result ^= (z += (hash64(data[i]) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
public int hash(final Object[] data) {
if (data == null)
return 0;
final long chips = $alt << 1 ^ 0xC6BC279692B5CC83L, len = data.length;
long z = 0x632BE59BD9B4E019L + chips, result = 1L;
Object o;
for (int i = 0; i < len; i++) {
o = data[i];
result ^= (z += ((o == null ? 0 : o.hashCode()) + 0x9E3779B97F4A7C15L) * 0xD0E89D2D311E289FL) * chips;
}
return (int) ((result ^= Long.rotateLeft((z * 0xC6BC279692B5CC83L ^ $alt ^ result * 0x9E3779B97F4A7C15L) + 0x632BE59BD9B4E019L, (int) (chips + z >>> 58))) ^ (result >>> 32));
}
}
/**
* An alternative hashing function that is slightly faster than CrossHash.Lightning, drastically
* faster than the default CrossHash methods (not in an inner class), and has good quality on most
* input, but has issues with certain methods (namely, {@link CrossHash.Falcon#hash(long[])} and
* {@link CrossHash.Falcon#hash(double[])} disregard the upper 32 bits, at the least, of any items
* in their input arrays, though the hash64 variants don't have this issue).
* <br>
* In most cases Lightning is a safe alternative, and is only slightly slower. If statistical
* quality or "salting" of the hash is particularly important, you should use {@link CrossHash.Storm}
* with a variety of salts/alterations.
* <br>
* Created by Tommy Ettinger on 1/16/2016.
*/
@Beta
public static final class Falcon {
public static long hash64(final boolean[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= (data[i] ? 0xC6BC279692B5CC83L : 0x789ABCDEFEDCBA98L) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final byte[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final short[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final char[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final int[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final long[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final float[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= NumberTools.floatToIntBits(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final double[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= NumberTools.doubleToLongBits(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final CharSequence data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length(); i++) {
result += (z ^= data.charAt(i) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i++) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = start; i < end && i < data.length; i += step) {
result += (z ^= data[i] * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final char[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash64(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final long[][] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash64(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final CharSequence[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash64(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash64(data[i]) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static long hash64(final Object[] data) {
if (data == null)
return 0;
long z = 0x632BE59BD9B4E019L, result = 1L;
Object o;
for (int i = 0; i < data.length; i++) {
o = data[i];
result += (z ^= (o == null ? 0 : o.hashCode()) * 0xD0E89D2D311E289FL) + 0x9E3779B97F4A7C15L;
}
return result ^ ((z ^ result) >>> 16) * 0x9E3779B97F4A7C15L;
}
public static int hash(final boolean[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= (data[i] ? 0x9E3779B9 : 0x789ABCDEL) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final byte[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final short[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final char[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final int[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
/**
* Be aware that this disregards the most-significant 32 bits of each long in data.
* Its use is discouraged, and if you need 32-bit hashes of long arrays, you should use
* {@link CrossHash.Lightning#hash(long[])} instead.
*
* @param data an array of long; be aware that this disregards a significant amount of data
* @return a 32-bit int hash of some of data
* @see CrossHash.Lightning#hash(long[]) You should prefer CrossHash.Lightning for this
*/
public static int hash(final long[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final float[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= NumberTools.floatToIntBits(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
/**
* Be aware that this disregards the most-significant 32 bits of the long
* representation of each double in data. Its use is discouraged, and if you
* need 32-bit hashes of double arrays, you should use
* {@link CrossHash.Lightning#hash(double[])} instead.
*
* @param data an array of double; be aware that this disregards a significant amount of data
* @return a 32-bit int hash of some of data
* @see CrossHash.Lightning#hash(double[]) You should prefer CrossHash.Lightning for this
*/
public static int hash(final double[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= NumberTools.doubleToLongBits(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final CharSequence data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length(); i++) {
result += (z ^= data.charAt(i) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = start; i < end && i < data.length; i++) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = start; i < end && i < data.length; i += step) {
result += (z ^= data[i] * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final char[][] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final long[][] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final CharSequence[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final CharSequence[]... data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
for (int i = 0; i < data.length; i++) {
result += (z ^= hash(data[i]) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
public static int hash(final Object[] data) {
if (data == null)
return 0;
int z = 0x632BE5AB, result = 1;
Object o;
for (int i = 0; i < data.length; i++) {
o = data[i];
result += (z ^= (o == null ? 0 : o.hashCode()) * 0x85157AF5) + 0x62E2AC0D;
}
return result ^ ((z ^ result) >>> 8) * 0x9E3779B9;
}
}
// Nice ints, all probable primes except the last one, for 32-bit hashing
// 0x62E2AC0D 0x632BE5AB 0x85157AF5 0x9E3779B9
/**
* The fastest hash in CrossHash, but no slouch on quality, either. Uses a finely-tuned mix of very few operations
* for each element, plus a minimal and simple finalization step, and as such obtains superior speed on the standard
* benchmark SquidLib uses for hashes (hashing one million 16-element long arrays, remaining the best in both 32-bit
* and 64-bit versions). Specifically, Wisp takes 9.478 ms to generate a million 64-bit hashes on a recent laptop
* with an i7-6700HQ processor (removing the time the control takes to generate the million arrays), whereas on the
* same setup the second-fastest hash, Falcon, takes 12.783 ms (also removing generation time). For comparison, the
* JDK's Arrays.hashCode method takes 13.642 ms on the same workload, though it produces 32-bit hashes. Wisp
* performs almost exactly as well producing 32-bit hashes as it does 64-bit hashes, where Falcon slows down
* slightly, and other hashes suffer a larger penalty producing 32-bit. This also avoids the quality issue in
* Falcon's 32-bit hashes of longs or doubles (all bits are considered here), and passes visual tests where an
* earlier version of Wisp did not. Collision rates are on-par with all other CrossHash classes and the JDK's
* Arrays.hashCode method, that is, acceptably low.
* <br>
* This version replaces an older version of Wisp that had serious quality issues and wasn't quite as fast. Since
* the only reason one would use the older version was speed without regard for quality, and it was marked as Beta,
* a faster version makes sense to replace the slower one, rather than add yet another nested class in CrossHash.
* <br>
* Wisp is no longer considered Beta-quality, and due to its speed and apparently very low collision rate in most
* arrays, it's recommended for usage in more places now. Code that used Lightning should in probably switch to Wisp
* if GWT is a potential target, since Wisp doesn't rely on having certain JVM optimizations that are probably only
* available on desktop platforms.
*/
public static final class Wisp {
public static long hash64(final boolean[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * (data[i] ? 0xC6BC279692B5CC83L : 0xAEF17502108EF2D9L));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final byte[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final short[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final char[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final int[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final long[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final float[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.floatToIntBits(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
/**
* The hashAlt and hash64Alt methods for floating-point number arrays have better visual hashing properties than
* hash and hash64, but are somewhat slower on desktop. They may be drastically faster than hash and hash64 on
* GWT, however, because they don't use {@link Double#doubleToLongBits(double)} or its equivalent for Floats,
* and those methods have much more complex implementations on GWT than on desktop Java.
* @param data a float array to hash
* @return a 64-bit hash code of data
*/
public static long hash64Alt(final float[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
double t;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * ((long) (-0xD0E8.9D2D311E289Fp-25 * (t = data[i]) + t * -0x1.39b4dce80194cp9)));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final double[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToLongBits(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
/**
* The hashAlt and hash64Alt methods for floating-point number arrays have better visual hashing properties than
* hash and hash64, but are somewhat slower on desktop. They may be drastically faster than hash and hash64 on
* GWT, however, because they don't use {@link Double#doubleToLongBits(double)} or its equivalent for Floats,
* and those methods have much more complex implementations on GWT than on desktop Java.
* @param data a double array to hash
* @return a 64-bit hash code of data
*/
public static long hash64Alt(final double[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
double t;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * ((long) (-0xD0E8.9D2D311E289Fp-25 * (t = data[i]) + t * -0x1.39b4dce80194cp9)));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final CharSequence data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length();
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data.charAt(i));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = end < data.length ? end : data.length;
for (int i = start; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final char[][] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final int[][] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final long[][] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final CharSequence[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i]));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
for (CharSequence datum : data) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(datum));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final List<? extends CharSequence> data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.size();
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data.get(i)));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final Object[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
Object o;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * ((o = data[i]) == null ? -1L : o.hashCode()));
}
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static long hash64(final Object data) {
if (data == null)
return 0L;
long a = 0x632BE59BD9B4E019L ^ 0x8329C6EB9E6AD3E3L * data.hashCode(), result = 0x9E3779B97F4A7C94L + a;
return result * (a | 1L) ^ (result >>> 27 | result << 37);
}
public static int hash(final boolean[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * (data[i] ? 0x789ABCDE : 0x62E2AC0D));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final byte[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]);
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final short[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]);
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final char[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]);
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final int[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]);
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final long[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]);
}
return (int)((result = (result * (a | 1L) ^ (result >>> 27 | result << 37))) ^ (result >>> 32));
}
/**
* This method is reasonable in quality and speed on desktop, though it has some visual hashing artifacts.
* The hashAlt method for float arrays has better visual hashing properties than this one, but it is somewhat
* slower on desktop. Because hashAlt turns out to be faster on GWT, super-sourcing is used to replace calls to
* {@link Wisp#hash(float[])} with calls to {@link Wisp#hashAlt(float[])} on GWT only. This changes the results
* between desktop/Android and GWT, but GWT was different anyway due to how it handles math being... "special."
* @param data a float array to hash
* @return a 32-bit hash code of data
*/
public static int hash(final float[] data) {
return NumberTools.hashWisp(data);
}
/**
* This method is identical to {@link Wisp#hash(float[])} on desktop, but unlike that method, it will not be
* super-sourced on GWT. This makes it slower (sometimes significantly so) on GWT, but using this method on GWT
* will behave closer to how it behaves on desktop than using {@link Wisp#hash(float[])} on GWT. GWT will
* probably not produce the same numbers anyway, because its math is "special."
* @param data a float array to hash
* @return a 32-bit hash code of data
*/
public static int hashAlt(final float[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
double t;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * ((int) (-0xD0E8.9D2D311E289Fp-25f * (t = data[i]) + t * -0x1.39b4dce80194cp9f)));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
/**
* This method is identical to {@link Wisp#hashAlt(double[])} on desktop, but is super-sourced on GWT, replacing
* the implementation only on that platform, to avoid math with 64-bit longs. This means this method, though not
* {@link Wisp#hashAlt(double[])}, will have different results for the same input on desktop/Android versus on
* GWT. GWT was different anyway, though, due to how it handles math being generally "special."
* @param data a double array to hash
* @return a 32-bit hash code of data
*/
public static int hash(final double[] data) {
return NumberTools.hashWisp(data);
}
/**
* This method is identical to {@link Wisp#hash(double[])} on desktop, but unlike that method, it will not be
* super-sourced on GWT. This makes it slower (sometimes significantly so) on GWT, but using this method on GWT
* will behave closer to how it behaves on desktop than using {@link Wisp#hash(double[])} on GWT. GWT will
* probably not produce the same numbers anyway, because its math is "special."
* @param data a double array to hash
* @return a 32-bit hash code of data
*/
public static int hashAlt(final double[] data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L;
final int len = data.length;
double t;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * ((long) (-0xD0E8.9D2D311E289Fp-25 * (t = data[i]) + t * -0x1.39b4dce80194cp9)));
}
return (int)((result = (result * (a | 1L) ^ (result >>> 27 | result << 37))) ^ (result >>> 32));
}
public static int hash(final CharSequence data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length();
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data.charAt(i));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = end < data.length ? end : data.length;
for (int i = start; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]);
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final char[][] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i]));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final int[][] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i]));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final long[][] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i]));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final CharSequence[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i]));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final CharSequence[]... data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i]));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
for (CharSequence datum : data) {
result += (a ^= 0x85157AF5 * hash(datum));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final List<? extends CharSequence> data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.size();
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data.get(i)));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final Object[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
Object o;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * ((o = data[i]) == null ? -1 : o.hashCode()));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
public static int hash(final Object data) {
if (data == null)
return 0;
int a = 0x632BE5AB ^ 0x85157AF5 * data.hashCode(), result = 0x9E3779B9 + a;
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
}
/**
* Strongly universal hashing based loosely on Daniel Lemire's (Apache-licensed) code and paper, available at
* https://github.com/lemire/StronglyUniversalStringHashing . This should have among the best statistical qualities
* of any of these hashes while being fairly fast. It is like {@link Storm} in that you need to instantiate a
* Chariot object to use its hashing functions, but unlike Storm's single long it uses to salt the hash, these
* objects have a somewhat-large, expansible cache of random numbers they use to modify every result differently
* based on its position in the input array. The cache's size is related to the largest array, String, or similar
* sequence that this has been required to hash, and the cache's size should be about 4 bytes per byte that needs to
* be hashed in a single input (so hashing only 16-byte arrays, of any number of such arrays, would require 64 bytes
* of cache). Technically, only slightly more than 2 bytes per byte are required, but this caches more random
* numbers in advance to speed up expected larger inputs. Only produces 32-bit hashes because of constraints on this
* strongly universal hash algorithm; you could create two or more of these with different seeds and run them on the
* same inputs to get more than 32 bits, although that would be rather slow.
* <br>
* For 32-bit hash functions where the function can be altered by a salt, CrossHash provides {@link Storm} and now
* Chariot, and assuming there aren't bugs in Chariot, this class could be preferable because it has about equal
* performance and the salt is a less-predictable 128 bits instead of 64 bits. If you need 64-bit hashes, you should
* use Storm instead unless you want to chain together two 32-bit hashes from Chariot (with 256 bits of salt).
* <br>
* This previously failed visual testing, but now a finalization step nicely eliminates artifacts on the hashes of
* similar points or inputs. It is now almost exactly the same speed as Storm (within the margin of error, or 50
* microseconds of difference per million hashes, and the quality is pretty much indistinguishable (which is good).
*/
@Beta
public static final class Chariot implements Serializable {
private static final long serialVersionUID = 3152426757973945155L;
private final long $alt0, $alt1;
private int top;
private transient long $tate0, $tate1;
private transient long[] $tore = null;
public Chariot() {
this(0L);
}
public Chariot(final CharSequence alteration) {
this(Falcon.hash64(alteration));
}
public Chariot(final long alteration0, final long alteration1) {
$alt0 = alteration0;
$alt1 = alteration1;
expand(32);
}
public Chariot(final long alteration) {
long state = alteration + 0x9E3779B97F4A7C15L,
z = state;
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
z ^= (z >>> 31);
$alt0 = z + 191 - Long.bitCount(z);
state += 0x9E3779B97F4A7C15L;
z = state;
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
z ^= (z >>> 31);
$alt1 = z + 191 - Long.bitCount(z);
expand(32);
}
private void expand(final int amount) {
if (amount <= 0)
return;
int done;
long z;
if ($tore == null) {
top = 32 + amount;
$tore = new long[top];
$tate0 = $alt0;
$tate1 = $alt1;
done = 0;
} else {
done = $tore.length;
top = done + amount;
long[] ls = new long[top];
System.arraycopy($tore, 0, ls, 0, done);
$tore = ls;
}
long s1;
for (; done < top; done++) {
final long s0 = $tate0;
s1 = $tate1;
$tore[done] = s0 + s1;
s1 ^= s0;
$tate0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b
$tate1 = Long.rotateLeft(s1, 36); // c
}
}
public static final Chariot alpha = new Chariot("alpha"), beta = new Chariot("beta"), gamma = new Chariot("gamma"),
delta = new Chariot("delta"), epsilon = new Chariot("epsilon"), zeta = new Chariot("zeta"),
eta = new Chariot("eta"), theta = new Chariot("theta"), iota = new Chariot("iota"),
kappa = new Chariot("kappa"), lambda = new Chariot("lambda"), mu = new Chariot("mu"),
nu = new Chariot("nu"), xi = new Chariot("xi"), omicron = new Chariot("omicron"), pi = new Chariot("pi"),
rho = new Chariot("rho"), sigma = new Chariot("sigma"), tau = new Chariot("tau"),
upsilon = new Chariot("upsilon"), phi = new Chariot("phi"), chi = new Chariot("chi"), psi = new Chariot("psi"),
omega = new Chariot("omega");
public static final Chariot[] predefined = new Chariot[]{alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota,
kappa, lambda, mu, nu, xi, omicron, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega};
public int hash(final boolean[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit >> 5) + 3 > top)
expand(top << 1 < (limit >> 5) + 3 ? (limit >> 5) + 3 : top);
long sum = $tore[0], t = 0L;
int i = 0, ii = 0;
for (; i + 31 < limit; i += 32) {
sum += (t = (data[i] ? 0x00000001L : 0L) | (data[i + 1] ? 0x00000002L : 0L)
| (data[i + 2] ? 0x00000004L : 0L) | (data[i + 3] ? 0x00000008L : 0L)
| (data[i + 4] ? 0x00000010L : 0L) | (data[i + 5] ? 0x00000020L : 0L)
| (data[i + 6] ? 0x00000040L : 0L) | (data[i + 7] ? 0x00000080L : 0L)
| (data[i + 8] ? 0x00000100L : 0L) | (data[i + 9] ? 0x00000200L : 0L)
| (data[i + 10] ? 0x00000400L : 0L) | (data[i + 11] ? 0x00000800L : 0L)
| (data[i + 12] ? 0x00001000L : 0L) | (data[i + 13] ? 0x00002000L : 0L)
| (data[i + 14] ? 0x00004000L : 0L) | (data[i + 15] ? 0x00008000L : 0L)
| (data[i + 16] ? 0x00010000L : 0L) | (data[i + 17] ? 0x00020000L : 0L)
| (data[i + 18] ? 0x00040000L : 0L) | (data[i + 19] ? 0x00080000L : 0L)
| (data[i + 20] ? 0x00100000L : 0L) | (data[i + 21] ? 0x00200000L : 0L)
| (data[i + 22] ? 0x00400000L : 0L) | (data[i + 23] ? 0x00800000L : 0L)
| (data[i + 24] ? 0x01000000L : 0L) | (data[i + 25] ? 0x02000000L : 0L)
| (data[i + 26] ? 0x04000000L : 0L) | (data[i + 27] ? 0x08000000L : 0L)
| (data[i + 28] ? 0x10000000L : 0L) | (data[i + 29] ? 0x20000000L : 0L)
| (data[i + 30] ? 0x40000000L : 0L) | (data[i + 31] ? 0x80000000L : 0L)
) * ($tore[++ii]);
}
if ((limit & 31) != 0) {
t = 0L;
for (int l = 0; l < (limit & 31); l++) {
t |= data[i++] ? 1L << l : 0L;
}
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final byte[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit >> 2) + 3 > top)
expand(top << 1 < (limit >> 2) + 3 ? (limit >> 2) + 3 : top);
long sum = $tore[0], t = 0L;
int i = 0, ii = 0;
for (; i + 3 < limit; i += 4) {
sum += (t = (data[i] & 0xFFL) | (data[i + 1] & 0xFFL) << 8
| (data[i + 2] & 0xFFL) << 16 | (data[i + 3] & 0xFFL) << 24) * ($tore[++ii]);
}
if ((limit & 3) != 0) {
t = 0L;
for (int l = 0; l < (limit & 3); l++) {
t |= (data[i++] & 0xFFL) << (l << 3);
}
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final short[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit >> 1) + 3 > top)
expand(top << 1 < (limit >> 1) + 3 ? (limit >> 1) + 3 : top);
long sum = $tore[0], t = 0L;
int i = 0, ii = 0;
for (; i + 1 < limit; i += 2) {
sum += (t = (data[i] & 0xFFFFL) | (data[i + 1] & 0xFFFFL) << 16) * ($tore[++ii]);
}
if ((limit & 1) != 0) {
t = data[i] & 0xFFFFL;
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final char[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit >> 1) + 3 > top)
expand(top << 1 < (limit >> 1) + 3 ? (limit >> 1) + 3 : top);
long sum = $tore[0], t = 0L;
int i = 0, ii = 0;
for (; i + 1 < limit; i += 2) {
sum += (t = (data[i] & 0xFFFFL) | (data[i + 1] & 0xFFFFL) << 16) * ($tore[++ii]);
}
if ((limit & 1) != 0) {
t = data[i] & 0xFFFFL;
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final char[] data, final int start, final int end) {
if (data == null)
return 0;
final int limit = end - start;
if ((limit >> 1) + 3 > top)
expand(top << 1 < (limit >> 1) + 3 ? (limit >> 1) + 3 : top);
long sum = $tore[0], t = 0L;
int i = start, ii = 0;
for (; i + 1 < end; i += 2) {
sum += (t = (data[i] & 0xFFFFL) | (data[i + 1] & 0xFFFFL) << 16) * ($tore[++ii]);
}
if ((limit & 1) != 0) {
t = data[i] & 0xFFFFL;
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final char[] data, final int start, final int end, final int step) {
if (data == null)
return 0;
final int limit = (end - start + step - 1) / step;
if ((limit >> 1) + 3 > top)
expand(top << 1 < (limit >> 1) + 3 ? (limit >> 1) + 3 : top);
long sum = $tore[0], t = 0L;
int i = start, ii = 0;
for (; i + 1 < end; i += step << 1) {
sum += (t = (data[i] & 0xFFFFL) | (data[i + step] & 0xFFFFL) << 16) * ($tore[++ii]);
}
if ((limit & 1) != 0) {
t = data[i] & 0xFFFFL;
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final int[] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (data[i] & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == 0)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final long[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit << 1) + 2 > top)
expand(top << 1 < (limit << 1) + 2 ? (limit << 1) + 2 : top);
long sum = $tore[0], t = 0;
for (int i = 0, ii = 1; i < limit; ii += 2) {
sum += ((t = data[i++]) & 0xFFFFFFFFL) * ($tore[ii]) + (t >>> 32) * ($tore[ii + 1]);
}
if (limit > 0 && (t >>> 32) == 0)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[(limit << 1) + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final float[] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (NumberTools.floatToIntBits(data[i]) & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == 0)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final double[] data) {
if (data == null)
return 0;
final int limit = data.length;
if ((limit << 1) + 2 > top)
expand(top << 1 < (limit << 1) + 2 ? (limit << 1) + 2 : top);
long sum = $tore[0], t = 0;
for (int i = 0, ii = 1; i < limit; ii += 2) {
sum += ((t = NumberTools.doubleToLongBits(data[i++])) & 0xFFFFFFFFL) * ($tore[ii]) + (t >>> 32) * ($tore[ii + 1]);
}
if (limit > 0 && (t >>> 32) == 0)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[(limit << 1) + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final CharSequence data) {
if (data == null)
return 0;
final int limit = data.length();
if ((limit >> 1) + 3 > top)
expand(top << 1 < (limit >> 1) + 3 ? (limit >> 1) + 3 : top);
long sum = $tore[0], t = 0L;
int i = 0, ii = 0;
for (; i + 1 < limit; i += 2) {
sum += (t = (data.charAt(i) & 0xFFFFL) | (data.charAt(i + 1) & 0xFFFFL) << 16) * ($tore[++ii]);
}
if ((limit & 1) != 0) {
t = data.charAt(i) & 0xFFFFL;
if (t == 0) sum += $tore[++ii] ^ 0x632BE59BD9B4E019L;
else sum += t * ($tore[++ii]);
} else if (limit > 0 && t == 0)
sum += $tore[ii] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[ii + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final char[][] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (hash(data[i]) & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == null)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final long[][] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (hash(data[i]) & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == null)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final CharSequence[] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (hash(data[i]) & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == null)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final CharSequence[]... data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
long sum = $tore[0];
for (int i = 0; i < limit; ) {
sum += (hash(data[i]) & 0xFFFFFFFFL) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == null)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
public int hash(final Object[] data) {
if (data == null)
return 0;
final int limit = data.length;
if (limit + 2 > top)
expand(top << 1 < limit + 2 ? limit + 2 : top);
Object o;
long sum = $tore[0];
for (int i = 0; i < limit; ) {
o = data[i];
sum += (o == null ? 0xFFFFFFFFL : (o.hashCode() & 0xFFFFFFFFL)) * ($tore[++i]);
}
if (limit > 0 && data[limit - 1] == null)
sum += $tore[limit] ^ 0x632BE59BD9B4E019L;
sum ^= $tore[limit + 1] + sum >>> (5 + (sum >>> 59));
return (int) (((sum *= 0xAEF17502108EF2D9L) >>> 43) ^ sum);
}
}
/**
* A whole cluster of Wisp-like hash functions that sacrifice a small degree of speed, but can be built with up
* to 128 bits of salt values that help to obscure what hashing function is actually being used. This class is
* similar to Storm in how you can construct one (using a CharSequence, one long to use to produce a salt, or in
* this class two longs to use to produce a salt), but differs from Storm by being somewhat faster, having many
* more possible salt "states" when using the constructors that take two longs or a CharSequence, and also by using
* 32-bit math when only 32-bit inputs and output are used (relevant for GWT with its slower 64-bit math).
* The salt values are mostly a pair of longs, but for the hash() functions that don't take a long array or double
* array, a different salt value is used, a pair of ints.
* <br>
* The salt fields are not serialized, so it is important that the same salt will be given by the
* program when the same hash results are wanted for some inputs.
* <br>
* A group of 24 static, final, pre-initialized Mist members are present in this class, each with the
* name of a letter in the Greek alphabet (this uses the convention on Wikipedia,
* https://en.wikipedia.org/wiki/Greek_alphabet#Letters , where lambda is spelled with a 'b'). The whole
* group of 24 pre-initialized members are also present in a static array called {@code predefined}.
* These can be useful when, for example, you want to get multiple hashes of a single array or String
* as part of cuckoo hashing or similar techniques that need multiple hashes for the same inputs.
*/
@Beta
public static final class Mist implements Serializable {
private static final long serialVersionUID = -1275284837479983271L;
private transient long $l1, $l2;
private transient int $i1, $i2;
public Mist() {
this(0x1234567876543210L, 0xEDCBA98789ABCDEFL);
}
public Mist(final CharSequence alteration) {
this(Wisp.hash64(alteration), Falcon.hash64(alteration));
}
private static int permute(final long state)
{
int s = (int)state ^ 0xD0E89D2D;
s = (s >>> 19 | s << 13);
s ^= state >>> (5 + (state >>> 59));
return ((s *= 277803737) >>> 22) ^ s;
}
@SuppressWarnings("NumericOverflow")
public Mist(final long alteration) {
$i1 = permute(alteration);
$l1 = alteration + $i1;
$l1 = ($l1 ^ ($l1 >>> 30)) * 0xBF58476D1CE4E5B9L;
$l1 = ($l1 ^ ($l1 >>> 27)) * 0x94D049BB133111EBL;
$l1 ^= $l1 >>> 31;
$i2 = permute($l1 + 0x9E3779B97F4A7C15L);
$l2 = alteration + 6 * 0x9E3779B97F4A7C15L;
$l2 = ($l2 ^ ($l2 >>> 30)) * 0xBF58476D1CE4E5B9L;
$l2 = ($l2 ^ ($l2 >>> 27)) * 0x94D049BB133111EBL;
$l2 ^= $l2 >>> 31;
}
@SuppressWarnings("NumericOverflow")
public Mist(final long alteration1, long alteration2) {
$i1 = permute(alteration1);
$l1 = alteration1 + $i1;
$i2 = permute(alteration2 + $i1);
$l2 = alteration2 + $i2;
}
/**
* Alters all of the salt values in a pseudo-random way based on the previous salt value.
* This will effectively make this Mist object a different, incompatible hashing functor.
* Meant for use in Cuckoo Hashing, which can need the hash function to be updated or changed.
* An alternative is to select a different Mist object from {@link #predefined}, or to simply
* construct a new Mist with a different parameter or set of parameters.
*/
@SuppressWarnings("NumericOverflow")
public void randomize()
{
$i1 = permute($l2 + 3 * 0x9E3779B97F4A7C15L);
$l1 = $l2 + $i1;
$l1 = ($l1 ^ ($l1 >>> 30)) * 0xBF58476D1CE4E5B9L;
$l1 = ($l1 ^ ($l1 >>> 27)) * 0x94D049BB133111EBL;
$l1 ^= $l1 >>> 31;
$i2 = permute($l1 + 5 * 0x9E3779B97F4A7C15L);
$l2 = $l1 + 6 * 0x9E3779B97F4A7C15L;
$l2 = ($l2 ^ ($l2 >>> 30)) * 0xBF58476D1CE4E5B9L;
$l2 = ($l2 ^ ($l2 >>> 27)) * 0x94D049BB133111EBL;
$l2 ^= $l2 >>> 31;
}
public static final Mist alpha = new Mist("alpha"), beta = new Mist("beta"), gamma = new Mist("gamma"),
delta = new Mist("delta"), epsilon = new Mist("epsilon"), zeta = new Mist("zeta"),
eta = new Mist("eta"), theta = new Mist("theta"), iota = new Mist("iota"),
kappa = new Mist("kappa"), lambda = new Mist("lambda"), mu = new Mist("mu"),
nu = new Mist("nu"), xi = new Mist("xi"), omicron = new Mist("omicron"), pi = new Mist("pi"),
rho = new Mist("rho"), sigma = new Mist("sigma"), tau = new Mist("tau"),
upsilon = new Mist("upsilon"), phi = new Mist("phi"), chi = new Mist("chi"), psi = new Mist("psi"),
omega = new Mist("omega");
public static final Mist[] predefined = new Mist[]{alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota,
kappa, lambda, mu, nu, xi, omicron, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega};
public long hash64(final boolean[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * (data[i] ? 0x9E3779B97F4A7C15L : 0x789ABCDEFEDCBA98L)) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final byte[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final short[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final char[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final int[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final long[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final float[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.floatToIntBits(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final double[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToLongBits(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = start; i < end && i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = start; i < end && i < len; i += step) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final CharSequence data) {
if (data == null)
return 0;
final int len = data.length();
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data.charAt(i)) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final char[][] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final long[][] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final CharSequence[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (CharSequence datum : data) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(datum)) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final CharSequence[]... data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * hash64(data[i])) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public long hash64(final Object[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
Object o;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * ((o = data[i]) == null ? -1 : o.hashCode())) ^ $l2 * a + $l1;
}
return result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37);
}
public int hash(final boolean[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * (data[i] ? 0x789ABCDE : 0x62E2AC0D)) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final byte[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final short[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final char[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final int[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final long[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * data[i]) ^ $l2 * a + $l1;
}
return (int)((result = (result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37))) ^ (result >>> 32));
}
public int hash(final float[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * NumberTools.floatToIntBits(data[i])) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final double[] data) {
if (data == null)
return 0;
final int len = data.length;
long result = 0x9E3779B97F4A7C94L + $l2, a = 0x632BE59BD9B4E019L;
for (int i = 0; i < len; i++) {
result += (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToLongBits(data[i])) ^ $l2 * a + $l1;
}
return (int)((result = (result * (a * $l1 | 1L) ^ (result >>> 27 | result << 37))) ^ (result >>> 32));
}
public int hash(final char[] data, final int start, final int end) {
if (data == null || start >= end)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = start; i < end && i < len; i++) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final char[] data, final int start, final int end, final int step) {
if (data == null || start >= end || step <= 0)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = start; i < end && i < len; i += step) {
result += (a ^= 0x85157AF5 * data[i]) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final CharSequence data) {
if (data == null)
return 0;
final int len = data.length();
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * data.charAt(i)) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final char[][] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i])) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final long[][] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i])) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final CharSequence[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i])) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final Iterable<? extends CharSequence> data) {
if (data == null)
return 0;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (CharSequence datum : data) {
result += (a ^= 0x85157AF5 * hash(datum)) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final CharSequence[]... data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data[i])) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final Object[] data) {
if (data == null)
return 0;
final int len = data.length;
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
Object o;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * ((o = data[i]) == null ? -1 : o.hashCode())) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final List<? extends CharSequence> data) {
if (data == null)
return 0;
final int len = data.size();
int result = 0x9E3779B9 + $i2, a = 0x632BE5AB;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * hash(data.get(i))) ^ $i2 * a + $i1;
}
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
public int hash(final Object data) {
if (data == null)
return 0;
int a = 0x632BE5AB ^ 0x85157AF5 * data.hashCode(), result = 0x9E3779B9 + $i2 + (a ^ $i2 * a + $i1);
return result * (a * $i1 | 1) ^ (result >>> 11 | result << 21);
}
}
}