113e3cdaf14a — Rune Skovbo Johansen 10 years ago
Refactor wrapping code for greater code reuse at cost of slightly increased overhead. Implement Wang Hash, Wang Double Hash, xxHash, and XorShift RNG.
23 files changed, 750 insertions(+), 205 deletions(-)

A => Assets/Implementations/HashFunctions.meta
A => Assets/Implementations/HashFunctions/HashFunction.cs
A => Assets/Implementations/HashFunctions/HashFunction.cs.meta
M Assets/Implementations/MurmurHash.cs => Assets/Implementations/HashFunctions/MurmurHash.cs
M Assets/Implementations/MurmurHash.cs.meta => Assets/Implementations/HashFunctions/MurmurHash.cs.meta
M Assets/Implementations/PcgHash.cs => Assets/Implementations/HashFunctions/PcgHash.cs
M Assets/Implementations/PcgHash.cs.meta => Assets/Implementations/HashFunctions/PcgHash.cs.meta
A => Assets/Implementations/HashFunctions/WangDoubleHash.cs
A => Assets/Implementations/HashFunctions/WangDoubleHash.cs.meta
A => Assets/Implementations/HashFunctions/WangHash.cs
A => Assets/Implementations/HashFunctions/WangHash.cs.meta
A => Assets/Implementations/HashFunctions/XXHash.cs
A => Assets/Implementations/HashFunctions/XXHash.cs.meta
A => Assets/Implementations/RandomNumberGenerators.meta
A => Assets/Implementations/RandomNumberGenerators/RandomNumberGenerator.cs
A => Assets/Implementations/RandomNumberGenerators/RandomNumberGenerator.cs.meta
A => Assets/Implementations/RandomNumberGenerators/XorShift.cs
A => Assets/Implementations/RandomNumberGenerators/XorShift.cs.meta
M Assets/Testing/RandomHashTest.cs => Assets/Testing/RandomnessTest.cs
M Assets/Testing/RandomHashTest.cs.meta => Assets/Testing/RandomnessTest.cs.meta
M Assets/Testing/RandomHashTester.cs => Assets/Testing/RandomnessTester.cs
M Assets/Testing/RandomHashTester.cs.meta => Assets/Testing/RandomnessTester.cs.meta
M Assets/UnityFrontend/NoiseTestMB.cs
A => Assets/Implementations/HashFunctions.meta +5 -0
@@ 0,0 1,5 @@ 
+fileFormatVersion: 2
+guid: fd4856a88958349989f59ad176bff7e0
+folderAsset: yes
+DefaultImporter:
+  userData: 

          
A => Assets/Implementations/HashFunctions/HashFunction.cs +70 -0
@@ 0,0 1,70 @@ 
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The Initial Developer of the Original Code is Rune Skovbo Johansen.
+ * Portions created by the Initial Developer are Copyright (C) 2015
+ * the Initial Developer. All Rights Reserved.
+ */
+
+using System;
+
+public abstract class HashFunction {
+	
+	// Main hash function for any number of parameters.
+	public abstract uint GetHash (params int[] data);
+	
+	// Optional optimizations for few parameters.
+	// Derived classes can optimize with custom implementations.
+	public virtual uint GetHash (int data) {
+		return GetHash (new int[] {data});
+	}
+	public virtual uint GetHash (int x, int y) {
+		return GetHash (new int[] {x, y});
+	}
+	public virtual uint GetHash (int x, int y, int z) {
+		return GetHash (new int[] {x, y, z});
+	}
+	
+	public float Value (params int[] data) {
+		return GetHash (data) / (float)uint.MaxValue;
+	}
+	// Potentially optimized overloads for few parameters.
+	public float Value (int data) {
+		return GetHash (data) / (float)uint.MaxValue;
+	}
+	public float Value (int x, int y) {
+		return GetHash (x, y) / (float)uint.MaxValue;
+	}
+	public float Value (int x, int y, int z) {
+		return GetHash (x, y, z) / (float)uint.MaxValue;
+	}
+	
+	public int Range (int min, int max, params int[] data) {
+		return min + (int)(GetHash (data) % (max - min));
+	}
+	// Potentially optimized overloads for few parameters.
+	public int Range (int min, int max, int data) {
+		return min + (int)(GetHash (data) % (max - min));
+	}
+	public int Range (int min, int max, int x, int y) {
+		return min + (int)(GetHash (x, y) % (max - min));
+	}
+	public int Range (int min, int max, int x, int y, int z) {
+		return min + (int)(GetHash (x, y, z) % (max - min));
+	}
+	
+	public float Range (float min, float max, params int[] data) {
+		return min + (GetHash (data) * (float)(max - min)) / uint.MaxValue;
+	}
+	// Potentially optimized overloads for few parameters.
+	public float Range (float min, float max, int data) {
+		return min + (GetHash (data) * (float)(max - min)) / uint.MaxValue;
+	}
+	public float Range (float min, float max, int x, int y) {
+		return min + (GetHash (x, y) * (float)(max - min)) / uint.MaxValue;
+	}
+	public float Range (float min, float max, int x, int y, int z) {
+		return min + (GetHash (x, y, z) * (float)(max - min)) / uint.MaxValue;
+	}
+};

          
A => Assets/Implementations/HashFunctions/HashFunction.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: 25f6de21eadf8436fb4fe9cc5f853bbd
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
M Assets/Implementations/MurmurHash.cs => Assets/Implementations/HashFunctions/MurmurHash.cs +51 -39
@@ 25,6 25,7 @@ 
  *    Removing all SQL dependencies (take byte array instead of SqlBinary).
  *    Adding overload that takes an int array (less code and executes faster).
  *    Adding methods for obtaining random integers or floats in given ranges.
+ *    Adding overload optimized for single int input with no loop.
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or

          
@@ 42,27 43,27 @@ 
 
 using System;
 
-public struct MurmurHash {
-	private UInt32 seed; /* Define your own seed here */
+public class MurmurHash : HashFunction {
+	private uint seed; /* Define your own seed here */
 	
-	public MurmurHash (int seedValue) {
-		seed = (UInt32)seedValue;
+	const uint c1 = 0xcc9e2d51;
+	const uint c2 = 0x1b873593;
+	
+	public MurmurHash (int seed) {
+		this.seed = (uint)seed;
 	}
 	
-	public Int32 GetHash (byte[] data) {
-		const UInt32 c1 = 0xcc9e2d51;
-		const UInt32 c2 = 0x1b873593;
-		
+	public uint GetHash (byte[] data) {
 		int curLength = data.Length; // Current position in byte array
 		int length = curLength; // The const length we need to fix tail
-		UInt32 h1 = seed;
-		UInt32 k1 = 0;
+		uint h1 = seed;
+		uint k1 = 0;
 		
 		// Body, eat stream a 32-bit int at a time
-		Int32 currentIndex = 0;
+		int currentIndex = 0;
 		while (curLength >= 4) {
-			// Get four bytes from the input into an UInt32
-			k1 = (UInt32)(data[currentIndex++] 
+			// Get four bytes from the input into an uint
+			k1 = (uint)(data[currentIndex++] 
 				| data[currentIndex++] << 8 
 				| data[currentIndex++] << 16 
 				| data[currentIndex++] << 24);

          
@@ 83,7 84,7 @@ public struct MurmurHash {
 		// because we can't fall through.)
 		switch (curLength) {
 			case 3:
-				k1 = (UInt32)(data[currentIndex++] 
+				k1 = (uint)(data[currentIndex++] 
 					| data[currentIndex++] << 8 
 					| data[currentIndex++] << 16);
 				k1 *= c1;

          
@@ 92,7 93,7 @@ public struct MurmurHash {
 				h1 ^= k1;
 				break;
 			case 2:
-				k1 = (UInt32)(data[currentIndex++] 
+				k1 = (uint)(data[currentIndex++] 
 					| data[currentIndex++] << 8);
 				k1 *= c1;
 				k1 = rotl32 (k1, 15);

          
@@ 100,7 101,7 @@ public struct MurmurHash {
 				h1 ^= k1;
 				break;
 			case 1:
-				k1 = (UInt32)(data[currentIndex++]);
+				k1 = (uint)(data[currentIndex++]);
 				k1 *= c1;
 				k1 = rotl32 (k1, 15);
 				k1 *= c2;

          
@@ 109,24 110,22 @@ public struct MurmurHash {
 		};
 		
 		// Finalization, magic chants to wrap it all up
-		h1 ^= (UInt32)length;
+		h1 ^= (uint)length;
 		h1 = fmix (h1);
 		
-		return (Int32)h1;
+		return h1;
 	}
 	
-	public Int32 GetHash (params int[] data) {
-		const UInt32 c1 = 0xcc9e2d51;
-		const UInt32 c2 = 0x1b873593;
-		
-		UInt32 h1 = seed;
-		UInt32 k1 = 0;
+	// Overload optimized for int input.
+	public override uint GetHash (params int[] data) {
+		uint h1 = seed;
+		uint k1 = 0;
 		
 		// Body, eat stream a 32-bit int at a time
 		int length = data.Length;
 		for (int i=0; i<length; i++) {
 			unchecked {
-				k1 = (UInt32)data[i];
+				k1 = (uint)data[i];
 			}
 			
 			// Bitmagic hash

          
@@ 140,29 139,42 @@ public struct MurmurHash {
 		}
 		
 		// Finalization, magic chants to wrap it all up
-		h1 ^= (UInt32)(length*4);
+		h1 ^= (uint)(length*4);
 		h1 = fmix (h1);
 		
-		return (Int32)h1;
-	}
-	
-	public float Value (params int[] data) {
-		return Math.Abs (GetHash (data)) / (float)int.MaxValue;
+		return h1;
 	}
 	
-	public int Range (int min, int max, params int[] data) {
-		return min + (int)(((UInt32)GetHash (data)) % (max - min));
+	// Overload optimized for single int input.
+	public override uint GetHash (int data) {
+		uint h1 = seed;
+		uint k1 = 0;
+		
+		unchecked {
+			k1 = (uint)data;
+		}
+		
+		// Bitmagic hash
+		k1 *= c1;
+		k1 = rotl32 (k1, 15);
+		k1 *= c2;
+		
+		h1 ^= k1;
+		h1 = rotl32 (h1, 13);
+		h1 = h1 * 5 + 0xe6546b64;
+		
+		// Finalization, magic chants to wrap it all up
+		h1 ^= 4U;
+		h1 = fmix (h1);
+		
+		return h1;
 	}
 	
-	public float Range (float min, float max, params int[] data) {
-		return min + ((UInt32)GetHash (data) * (float)(max - min)) / UInt32.MaxValue;
-	}
-	
-	private static UInt32 rotl32 (UInt32 x, byte r) {
+	private static uint rotl32 (uint x, byte r) {
 		return (x << r) | (x >> (32 - r));
 	}
 	
-	private static UInt32 fmix (UInt32 h) {
+	private static uint fmix (uint h) {
 		h ^= h >> 16;
 		h *= 0x85ebca6b;
 		h ^= h >> 13;

          
M Assets/Implementations/MurmurHash.cs.meta => Assets/Implementations/HashFunctions/MurmurHash.cs.meta +0 -0

        
M Assets/Implementations/PcgHash.cs => Assets/Implementations/HashFunctions/PcgHash.cs +13 -5
@@ 9,25 9,33 @@ 
 
 using System;
 
-public static class PcgHash {
-	public static int Hash (int i, int j) {
+public class PcgHash : HashFunction {
+	
+	public override uint GetHash (params int[] data) {
+		if (data.Length >= 2)
+			return GetHash (data[0], data[1]);
+		else
+			return GetHash (data[0]);
+	}
+	
+	public override uint GetHash (int i, int j) {
 		int a = i;
 		int b = j;
 		for (int r = 0; r < 3; r++) {
 			a = rot ((int) ((a^0xcafebabe) + (b^0xfaceb00c)), 23);
 			b = rot ((int) ((a^0xdeadbeef) + (b^0x8badf00d)), 5);
 		}
-		return a^b;
+		return (uint)(a^b);
 	}
 	
-	public static int Hash (int i) {
+	public override uint GetHash (int i) {
 		int a = i;
 		int b = 0;
 		for (int r = 0; r < 3; r++) {
 			a = rot ((int) ((a^0xcafebabe) + (b^0xfaceb00c)), 23);
 			b = rot ((int) ((a^0xdeadbeef) + (b^0x8badf00d)), 5);
 		}
-		return a^b;
+		return (uint)(a^b);
 	}
 	
 	private static int rot (int x, int b) {

          
M Assets/Implementations/PcgHash.cs.meta => Assets/Implementations/HashFunctions/PcgHash.cs.meta +0 -0

        
A => Assets/Implementations/HashFunctions/WangDoubleHash.cs +50 -0
@@ 0,0 1,50 @@ 
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The Initial Developer of the Original Code is Rune Skovbo Johansen.
+ * Portions created by the Initial Developer are Copyright (C) 2015
+ * the Initial Developer. All Rights Reserved.
+ */
+
+using System;
+
+public class WangDoubleHash : HashFunction {
+	private uint seed;
+	
+	public WangDoubleHash (int seedValue) {
+		seed = GetHashOfInt ((uint)seedValue);
+	}
+	
+	public override uint GetHash (int data) {
+		return GetHashOfInt (seed ^ (uint)data);
+	}
+	
+	public override uint GetHash (params int[] data) {
+		uint val = seed;
+		for (int i=0; i<data.Length; i++)
+			val = GetHashOfInt (val ^ (uint)data[i]);
+		return val;
+	}
+	
+	private uint GetHashOfInt (uint data) {
+		uint val = data;
+		
+		// Based on Thomas Wang’s integer hash functions.
+		// Applied twice.
+		
+		val = (val ^ 61) ^ (val >> 16);
+		val *= 9;
+		val = val ^ (val >> 4);
+		val *= 0x27d4eb2d;
+		val = val ^ (val >> 15);
+		
+		val = (val ^ 61) ^ (val >> 16);
+		val *= 9;
+		val = val ^ (val >> 4);
+		val *= 0x27d4eb2d;
+		val = val ^ (val >> 15);
+		
+		return val;
+	}
+};

          
A => Assets/Implementations/HashFunctions/WangDoubleHash.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: 85bb016f8330c408b98ab49ae3325854
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
A => Assets/Implementations/HashFunctions/WangHash.cs +40 -0
@@ 0,0 1,40 @@ 
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The Initial Developer of the Original Code is Rune Skovbo Johansen.
+ * Portions created by the Initial Developer are Copyright (C) 2015
+ * the Initial Developer. All Rights Reserved.
+ */
+
+using System;
+
+public class WangHash : HashFunction {
+	private uint seed;
+	
+	public WangHash (int seedValue) {
+		seed = GetHashOfInt ((uint)seedValue);
+	}
+	
+	public override uint GetHash (int data) {
+		return GetHashOfInt (seed ^ (uint)data);
+	}
+	
+	public override uint GetHash (params int[] data) {
+		uint val = seed;
+		for (int i=0; i<data.Length; i++)
+			val = GetHashOfInt (val ^ (uint)data[i]);
+		return val;
+	}
+	
+	private uint GetHashOfInt (uint data) {
+		uint val = data;
+		// Based on Thomas Wang’s integer hash functions.
+		val = (val ^ 61) ^ (val >> 16);
+		val *= 9;
+		val = val ^ (val >> 4);
+		val *= 0x27d4eb2d;
+		val = val ^ (val >> 15);
+		return val;
+	}
+};

          
A => Assets/Implementations/HashFunctions/WangHash.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: e9ab3721b2bf94dc1991bac75599b4c8
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
A => Assets/Implementations/HashFunctions/XXHash.cs +225 -0
@@ 0,0 1,225 @@ 
+/*
+C# implementation of xxHash optimized for producing random numbers from one or more input integers.
+Copyright (C) 2015, Rune Skovbo Johansen. (https://bitbucket.org/runevision/random-numbers-testing/)
+
+Based on C# implementation Copyright (C) 2014, Seok-Ju, Yun. (https://github.com/noricube/xxHashSharp)
+
+Original C Implementation Copyright (C) 2012-2014, Yann Collet. (https://code.google.com/p/xxhash/)
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+using System;
+
+public class XXHash : HashFunction {
+	private uint seed;
+	
+	const uint PRIME32_1 = 2654435761U;
+	const uint PRIME32_2 = 2246822519U;
+	const uint PRIME32_3 = 3266489917U;
+	const uint PRIME32_4 = 668265263U;
+	const uint PRIME32_5 = 374761393U;
+	
+	public XXHash (int seed) {
+		this.seed = (uint)seed;
+	}
+	
+	public uint GetHash (byte[] buf) {
+		uint h32;
+		int index = 0;
+		int len = buf.Length;
+		
+		if (len >= 16) {
+			int limit = len - 16;
+			uint v1 = seed + PRIME32_1 + PRIME32_2;
+			uint v2 = seed + PRIME32_2;
+			uint v3 = seed + 0;
+			uint v4 = seed - PRIME32_1;
+			
+			do {
+				v1 = CalcSubHash (v1, buf, index);
+				index += 4;
+				v2 = CalcSubHash (v2, buf, index);
+				index += 4;
+				v3 = CalcSubHash (v3, buf, index);
+				index += 4;
+				v4 = CalcSubHash (v4, buf, index);
+				index += 4;
+			} while (index <= limit);
+			
+			h32 = RotateLeft (v1, 1) + RotateLeft (v2, 7) + RotateLeft (v3, 12) + RotateLeft (v4, 18);
+		}
+		else {
+			h32 = seed + PRIME32_5;
+		}
+		
+		h32 += (uint)len;
+		
+		while (index <= len - 4) {
+			h32 += BitConverter.ToUInt32 (buf, index) * PRIME32_3;
+			h32 = RotateLeft (h32, 17) * PRIME32_4;
+			index += 4;
+		}
+		
+		while (index<len) {
+			h32 += buf[index] * PRIME32_5;
+			h32 = RotateLeft (h32, 11) * PRIME32_1;
+			index++;
+		}
+		
+		h32 ^= h32 >> 15;
+		h32 *= PRIME32_2;
+		h32 ^= h32 >> 13;
+		h32 *= PRIME32_3;
+		h32 ^= h32 >> 16;
+		
+		return h32;
+	}
+	
+	public uint GetHash (params uint[] buf) {
+		uint h32;
+		int index = 0;
+		int len = buf.Length;
+		
+		if (len >= 4) {
+			int limit = len - 4;
+			uint v1 = seed + PRIME32_1 + PRIME32_2;
+			uint v2 = seed + PRIME32_2;
+			uint v3 = seed + 0;
+			uint v4 = seed - PRIME32_1;
+			
+			do {
+				v1 = CalcSubHash (v1, buf[index]);
+				index++;
+				v2 = CalcSubHash (v2, buf[index]);
+				index++;
+				v3 = CalcSubHash (v3, buf[index]);
+				index++;
+				v4 = CalcSubHash (v4, buf[index]);
+				index++;
+			} while (index <= limit);
+			
+			h32 = RotateLeft (v1, 1) + RotateLeft (v2, 7) + RotateLeft (v3, 12) + RotateLeft (v4, 18);
+		}
+		else {
+			h32 = seed + PRIME32_5;
+		}
+		
+		h32 += (uint)len * 4;
+		
+		while (index < len) {
+			h32 += buf[index] * PRIME32_3;
+			h32 = RotateLeft (h32, 17) * PRIME32_4;
+			index++;
+		}
+		
+		h32 ^= h32 >> 15;
+		h32 *= PRIME32_2;
+		h32 ^= h32 >> 13;
+		h32 *= PRIME32_3;
+		h32 ^= h32 >> 16;
+		
+		return h32;
+	}
+	
+	public override uint GetHash (params int[] buf) {
+		uint h32;
+		int index = 0;
+		int len = buf.Length;
+		
+		if (len >= 4) {
+			int limit = len - 4;
+			uint v1 = (uint)seed + PRIME32_1 + PRIME32_2;
+			uint v2 = (uint)seed + PRIME32_2;
+			uint v3 = (uint)seed + 0;
+			uint v4 = (uint)seed - PRIME32_1;
+			
+			do {
+				v1 = CalcSubHash (v1, (uint)buf[index]);
+				index++;
+				v2 = CalcSubHash (v2, (uint)buf[index]);
+				index++;
+				v3 = CalcSubHash (v3, (uint)buf[index]);
+				index++;
+				v4 = CalcSubHash (v4, (uint)buf[index]);
+				index++;
+			} while (index <= limit);
+			
+			h32 = RotateLeft (v1, 1) + RotateLeft (v2, 7) + RotateLeft (v3, 12) + RotateLeft (v4, 18);
+		}
+		else {
+			h32 = (uint)seed + PRIME32_5;
+		}
+		
+		h32 += (uint)len * 4;
+		
+		while (index < len) {
+			h32 += (uint)buf[index] * PRIME32_3;
+			h32 = RotateLeft (h32, 17) * PRIME32_4;
+			index++;
+		}
+		
+		h32 ^= h32 >> 15;
+		h32 *= PRIME32_2;
+		h32 ^= h32 >> 13;
+		h32 *= PRIME32_3;
+		h32 ^= h32 >> 16;
+		
+		return h32;
+	}
+	
+	public override uint GetHash (int buf) {
+		uint h32 = (uint)seed + PRIME32_5;
+		h32 += 4U;
+		h32 += (uint)buf * PRIME32_3;
+		h32 = RotateLeft (h32, 17) * PRIME32_4;
+		h32 ^= h32 >> 15;
+		h32 *= PRIME32_2;
+		h32 ^= h32 >> 13;
+		h32 *= PRIME32_3;
+		h32 ^= h32 >> 16;
+		return h32;
+	}
+	
+	private static uint CalcSubHash (uint value, byte[] buf, int index) {
+		uint read_value = BitConverter.ToUInt32 (buf, index);
+		value += read_value * PRIME32_2;
+		value = RotateLeft (value, 13);
+		value *= PRIME32_1;
+		return value;
+	}
+	
+	private static uint CalcSubHash (uint value, uint read_value) {
+		value += read_value * PRIME32_2;
+		value = RotateLeft (value, 13);
+		value *= PRIME32_1;
+		return value;
+	}
+	
+	private static uint RotateLeft (uint value, int count) {
+		return (value << count) | (value >> (32 - count));
+	}
+}
+

          
A => Assets/Implementations/HashFunctions/XXHash.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: 1d66b545af4ee4205a235e05e8a3f64d
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
A => Assets/Implementations/RandomNumberGenerators.meta +5 -0
@@ 0,0 1,5 @@ 
+fileFormatVersion: 2
+guid: 749382762b2a24138a6c8a2fb048d65c
+folderAsset: yes
+DefaultImporter:
+  userData: 

          
A => Assets/Implementations/RandomNumberGenerators/RandomNumberGenerator.cs +27 -0
@@ 0,0 1,27 @@ 
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The Initial Developer of the Original Code is Rune Skovbo Johansen.
+ * Portions created by the Initial Developer are Copyright (C) 2015
+ * the Initial Developer. All Rights Reserved.
+ */
+
+using System;
+
+public abstract class RandomNumberGenerator {
+	
+	public abstract uint Next ();
+	
+	public float Value () {
+		return Next () / (float)uint.MaxValue;
+	}
+	
+	public int Range (int min, int max) {
+		return min + (int)(Next () % (max - min));
+	}
+	
+	public float Range (float min, float max) {
+		return min + (Next () * (float)(max - min)) / uint.MaxValue;
+	}
+};

          
A => Assets/Implementations/RandomNumberGenerators/RandomNumberGenerator.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: 6a26593cf134645ff8f831d44e1ff83a
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
A => Assets/Implementations/RandomNumberGenerators/XorShift.cs +26 -0
@@ 0,0 1,26 @@ 
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The Initial Developer of the Original Code is Rune Skovbo Johansen.
+ * Portions created by the Initial Developer are Copyright (C) 2015
+ * the Initial Developer. All Rights Reserved.
+ */
+
+using System;
+
+public class XorShift : RandomNumberGenerator {
+	private uint state;
+	
+	public XorShift (int seed) {
+		state = (uint)seed;
+	}
+	
+	public override uint Next () {
+		// Xorshift algorithm from George Marsaglia's paper.
+		state ^= (state << 13);
+		state ^= (state >> 17);
+		state ^= (state << 5);
+		return state;
+	}
+};

          
A => Assets/Implementations/RandomNumberGenerators/XorShift.cs.meta +8 -0
@@ 0,0 1,8 @@ 
+fileFormatVersion: 2
+guid: e0f23f24cbfaa44a8b53e4a06e53e059
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 

          
M Assets/Testing/RandomHashTest.cs => Assets/Testing/RandomnessTest.cs +25 -14
@@ 12,31 12,38 @@ using System.Diagnostics;
 using System.Collections.Generic;
 
 public interface RandomSequence {
-	int Next ();
+	uint Next ();
 	void Reset ();
 	string GetName ();
 }
 
-public class RandomHashTest {
+public class RandomnessTest {
 	public RandomSequence sequence;
 	public string name;
 	public string result;
+	public float[] noiseSequence = new float[256*256];
 	public float[,] coordsArray = new float[256,256];
 	public float[] diagonalSums = new float[256];
 	public float diagonalsDeviation = 0;
+	public int byteIndex = 0;
 	
-	public RandomHashTest (RandomSequence randomSequence) {
+	public RandomnessTest (RandomSequence randomSequence) {
 		sequence = randomSequence;
 		name = sequence.GetName ().Replace ("#", "0\u2026n");
 		Test ();
+		Console.WriteLine ("\n"+name);
 		Console.WriteLine (result);
 	}
 	
-	public int Function256 () {
-		return GetInRange (sequence.Next (), 256);
+	public uint GetNext () {
+		return sequence.Next ();
 	}
 	
-	public int GetInRange (int i, int range) {
+	public uint GetBytePart (uint i, int byteIndex) {
+		return (((i >> (8 * byteIndex)) % 256) + 256) % 256;
+	}
+	
+	public uint GetInRange (uint i, uint range) {
 		return ((i % range) + range) % range;
 	}
 

          
@@ 45,7 52,7 @@ public class RandomHashTest {
 	}
 
 	private void Test () {
-		int[] ints = new int[500000];
+		uint[] ints = new uint[500000];
 		
 		// Call random function
 		sequence.Reset ();

          
@@ 58,19 65,23 @@ public class RandomHashTest {
 		// Convert to bytes
 		byte[] bytes = new byte[ints.Length];
 		for (int i = 0; i < bytes.Length; i++)
-			bytes[i] = (byte)GetInRange (ints[i], 256);
+			bytes[i] = (byte)GetBytePart (ints[i], byteIndex);
 		
 		// Test randomness data
 		Ent.EntCalc ent = new Ent.EntCalc (false);
 		ent.AddSample (bytes, false);
 		Ent.EntCalc.EntCalcResult calcResult = ent.EndCalculation ();
-
+		
+		// Create noise sequence
+		for (int i=0; i<noiseSequence.Length; i++)
+			noiseSequence[i] = GetBytePart (ints[i], byteIndex) / 255f;
+		
 		// Create coords data
 		Reset ();
 		float samplesPerPixelInverse = (256f * 256f) / ints.Length;
 		for (int i=0; i<ints.Length; i+=6) {
-			int x = Function256 ();
-			int y = Function256 ();
+			uint x = GetBytePart (GetNext (), byteIndex);
+			uint y = GetBytePart (GetNext (), byteIndex);
 			coordsArray[x,y] += samplesPerPixelInverse;
 		}
 

          
@@ 85,7 96,7 @@ public class RandomHashTest {
 		diagonalsDeviation = StandardDeviation (new List<float> (diagonalSums));
 
 		// Get string with result
-		result = GetResult (name, calcResult, stopWatch.ElapsedMilliseconds);
+		result = GetResult (calcResult, stopWatch.ElapsedMilliseconds);
 	}
 
 	public static float StandardDeviation (List<float> valueList) {

          
@@ 102,7 113,7 @@ public class RandomHashTest {
 		return (float)System.Math.Sqrt (s / (k-1));
 	}
 	
-	string GetResult (string name, Ent.EntCalc.EntCalcResult result, float duration) {
+	string GetResult (Ent.EntCalc.EntCalcResult result, float duration) {
 		double meanValueQuality = Clamp01 (1 - Math.Abs (127.5 - result.Mean) / 128);
 		double serialCorrelationQuality = Clamp01 (1 - 2 * Math.Abs (result.SerialCorrelation));
 		double piQuality = Clamp01 (1 - 10 * result.MonteCarloErrorPct);

          
@@ 120,7 131,7 @@ public class RandomHashTest {
 			+ "Execution Time:                  {9,6} ms",
 
 			result.Mean, meanValueQuality,
-			result.SerialCorrelation, serialCorrelationQuality,
+			Math.Max (0, result.SerialCorrelation), serialCorrelationQuality,
 			result.MonteCarloPiCalc, piQuality,
 			diagonalsDeviation, diagonalsDeviationQuality,
 			combined,

          
M Assets/Testing/RandomHashTest.cs.meta => Assets/Testing/RandomnessTest.cs.meta +0 -0

        
M Assets/Testing/RandomHashTester.cs => Assets/Testing/RandomnessTester.cs +160 -142
@@ 11,209 11,227 @@ using System;
 using System.Collections.Generic;
 using System.Security.Cryptography;
 
-public class RandomHashTester {
-	public List<RandomHashTest> tests = new List<RandomHashTest> ();
+public class RandomnessTester {
+	public List<RandomnessTest> tests = new List<RandomnessTest> ();
 
-	public RandomHashTester () {
-		AddTest (new SystemRandomWrapper (0));
-		AddTest (new SystemRandomSeedVarianceWrapper (0));
-		AddTest (new SystemRandomSeedVarianceWrapper (100));
-		AddTest (new SystemRandomScrambledSeedVarianceWrapper (0));
-		AddTest (new LinearFunction (19969, 207));
-		AddTest (new PcgHashWrapper ());
-		AddTest (new MD5HashWrapper ());
-		AddTest (new MurmurHashWrapper (0));
-		AddTest (new MurmurHashSeedVarianceWrapper (0));
+	public RandomnessTester () {
+		HashWrapper linear = new HashWrapper (
+			"Linear function i * 19969 / 207",
+			false,
+			seed => null,
+			(gen, index) => (uint)index * 19969 / 207);
+		
+		RNGWrapper systemRandom = new RNGWrapper (
+			"System.Random",
+			true,
+			seed => new Random (seed),
+			gen => (uint)((Random)gen).Next ());
+		
+		RNGWrapper systemRandomScrambled = new RNGWrapper (
+			"System.Random (Scrambled)",
+			true,
+			seed => new Random ((int)((seed ^ 0x5DEECE66DL) & ((1L << 48) - 1))),
+			gen => (uint)((Random)gen).Next ());
+		
+		RNGWrapper xorShift = new RNGWrapper (
+			"XorShift",
+			true,
+			seed => new XorShift (seed),
+			gen => ((XorShift)gen).Next ());
+		
+		HashWrapper pcgHash = new HashWrapper (
+			"PcgHash",
+			false,
+			seed => new PcgHash (),
+			(gen, index) => ((PcgHash)gen).GetHash (index));
+		
+		HashWrapper md5 = new HashWrapper (
+			"MD5",
+			false,
+			seed => MD5.Create (),
+			(gen, index) => BitConverter.ToUInt32 (((MD5)gen).ComputeHash (BitConverter.GetBytes (index)), 0));
+		
+		HashWrapper murmur3 = new HashWrapper (
+			"MurmurHash3",
+			true,
+			seed => new MurmurHash (seed),
+			(gen, index) => ((MurmurHash)gen).GetHash (index));
+		
+		HashWrapper xxHash = new HashWrapper (
+			"xxHash",
+			true,
+			seed => new XXHash (seed),
+			(gen, index) => ((XXHash)gen).GetHash (index));
+		
+		HashWrapper wangHash = new HashWrapper (
+			"WangHash",
+			true,
+			seed => new WangHash (seed),
+			(gen, index) => ((WangHash)gen).GetHash (index));
+		
+		HashWrapper wangDoubleHash = new HashWrapper (
+			"WangDoubleHash",
+			true,
+			seed => new WangDoubleHash (seed),
+			(gen, index) => ((WangDoubleHash)gen).GetHash (index));
+		
+		// RNGs
+		AddTest (new RNGSequence (systemRandom, 0));
+		AddTest (new RNGSequence (xorShift, 1));
+		
+		// RNGs with varying seeds
+		AddTest (new RNGVarySeedSequence (systemRandom, 0));
+		AddTest (new RNGVarySeedSequence (systemRandom, 100));
+		AddTest (new RNGVarySeedSequence (systemRandomScrambled, 0));
+		
+		// Hash functions
+		AddTest (new HashSequence (pcgHash, 0));
+		AddTest (new HashSequence (md5, 0));
+		AddTest (new HashSequence (murmur3, 0));
+		AddTest (new HashVarySeedSequence (murmur3, 0));
+		AddTest (new HashSequence (xxHash, 0));
+		AddTest (new HashVarySeedSequence (xxHash, 0));
+		AddTest (new HashSequence (wangHash, 0));
+		AddTest (new HashSequence (wangDoubleHash, 0));
+		AddTest (new HashVarySeedSequence (wangDoubleHash, 0));
+		
+		// Dumb linear function for reference
+		AddTest (new HashSequence (linear, 0));
 	}
 
 	private void AddTest (RandomSequence sequence) {
-		tests.Add (new RandomHashTest (sequence));
+		tests.Add (new RandomnessTest (sequence));
+	}
+}
+
+public class RNGWrapper {
+	public delegate object GetGenerator (int seed);
+	public delegate uint GetNext (object generator);
+	public string name;
+	public bool supportsSeed;
+	public GetGenerator getGenerator;
+	public GetNext getNext;
+	public RNGWrapper (string name, bool supportsSeed, GetGenerator generator, GetNext next) {
+		this.name = name;
+		this.supportsSeed = supportsSeed;
+		getGenerator = generator;
+		getNext = next;
 	}
 }
 
-public class SystemRandomWrapper : RandomSequence {
+public class HashWrapper {
+	public delegate object GetGenerator (int seed);
+	public delegate uint GetHash (object generator, int input);
+	public string name;
+	public bool supportsSeed;
+	public GetGenerator getGenerator;
+	public GetHash getHash;
+	public HashWrapper (string name, bool supportsSeed, GetGenerator generator, GetHash hash) {
+		this.name = name;
+		this.supportsSeed = supportsSeed;
+		getGenerator = generator;
+		getHash = hash;
+	}
+}
+
+public class RNGSequence : RandomSequence {
+	private RNGWrapper wrapper;
 	private int seed;
-	private Random rand;
-	public SystemRandomWrapper (int seed) {
+	private object generator;
+	public RNGSequence (RNGWrapper wrapper, int seed) {
+		this.wrapper = wrapper;
 		this.seed = seed;
 		Reset ();
 	}
 	public void Reset () {
-		rand = new Random (seed);
+		generator = wrapper.getGenerator (seed);
 	}
-	public int Next () {
-		return rand.Next ();
+	public uint Next () {
+		return wrapper.getNext (generator);
 	}
 	public string GetName () {
-		return string.Format ("System.Random\nnumbers # of seed {0}", seed);
+		if (wrapper.supportsSeed)
+			return string.Format ("{0}\nnumbers # of seed {1}", wrapper.name, seed);
+		else
+			return string.Format ("{0}\nnumbers #", wrapper.name);
 	}
 }
 
-public class SystemRandomSeedVarianceWrapper : RandomSequence {
+public class RNGVarySeedSequence : RandomSequence {
+	private RNGWrapper wrapper;
 	private int index;
 	private int seed;
-	public SystemRandomSeedVarianceWrapper (int index) {
-		this.index = index;
-		Reset ();
-	}
-	public void Reset () {
-		seed = 0;
-	}
-	public int Next () {
-		Random rand = new Random (seed);
-		seed++;
-		// Get the number in the sequence corresponding to index.
-		// This is super slow for large index values!
-		for (int i=0; i<index; i++)
-			rand.Next ();
-		return rand.Next ();
-	}
-	public string GetName () {
-		return string.Format ("System.Random\n{0} number of seed #", index.Ordinal ());
-	}
-}
-
-public class SystemRandomScrambledWrapper : RandomSequence {
-	private int seed;
-	private Random rand;
-	public SystemRandomScrambledWrapper (int seed) {
-		this.seed = (int)((seed ^ 0x5DEECE66DL) & ((1L << 48) - 1));
-		Reset ();
-	}
-	public void Reset () {
-		rand = new Random (seed);
-	}
-	public int Next () {
-		return rand.Next ();
-	}
-	public string GetName () {
-		return string.Format ("System.Random (Scrambled)\nnumbers # of seed {0}", seed);
-	}
-}
-
-public class SystemRandomScrambledSeedVarianceWrapper : RandomSequence {
-	private int index;
-	private int seed;
-	public SystemRandomScrambledSeedVarianceWrapper (int index) {
+	public RNGVarySeedSequence (RNGWrapper wrapper, int index) {
+		if (!wrapper.supportsSeed)
+			throw new System.Exception ("RNGWrapper for RNGVarySeedSequence must support seed.");
+		this.wrapper = wrapper;
 		this.index = index;
 		Reset ();
 	}
 	public void Reset () {
 		seed = 0;
 	}
-	public int Next () {
-		int seedModified = (int)((seed ^ 0x5DEECE66DL) & ((1L << 48) - 1));
-		Random rand = new Random (seedModified);
+	public uint Next () {
+		object generator = wrapper.getGenerator (seed);
 		seed++;
 		// Get the number in the sequence corresponding to index.
 		// This is super slow for large index values!
 		for (int i=0; i<index; i++)
-			rand.Next ();
-		return rand.Next ();
+			wrapper.getNext (generator);
+		return wrapper.getNext (generator);
 	}
 	public string GetName () {
-		return string.Format ("System.Random (Scrambled)\n{0} number of seed #", index.Ordinal ());
+		return string.Format ("{0}\n{1} number of seed #", wrapper.name, index.Ordinal ());
 	}
 }
 
-public class MurmurHashWrapper : RandomSequence {
+public class HashSequence : RandomSequence {
+	private HashWrapper wrapper;
 	private int seed;
-	private MurmurHash hash;
+	private object generator;
 	private int index;
-	public MurmurHashWrapper (int seed) {
+	public HashSequence (HashWrapper wrapper, int seed) {
+		this.wrapper = wrapper;
 		this.seed = seed;
-		hash = new MurmurHash (seed);
+		generator = wrapper.getGenerator (seed);
 		Reset ();
 	}
 	public void Reset () {
 		index = 0;
 	}
-	public int Next () {
-		int value = hash.GetHash (index);
+	public uint Next () {
+		uint value = wrapper.getHash (generator, index);
 		index++;
 		return value;
 	}
 	public string GetName () {
-		return string.Format ("MurmurHash3\nnumbers # of seed {0}", seed);
+		if (wrapper.supportsSeed)
+			return string.Format ("{0}\nnumbers # of seed {1}", wrapper.name, seed);
+		else
+			return string.Format ("{0}\nnumbers #", wrapper.name);
 	}
 }
 
-public class MurmurHashSeedVarianceWrapper : RandomSequence {
+public class HashVarySeedSequence : RandomSequence {
+	private HashWrapper wrapper;
 	private int index;
 	private int seed;
-	public MurmurHashSeedVarianceWrapper (int index) {
+	public HashVarySeedSequence (HashWrapper wrapper, int index) {
+		if (!wrapper.supportsSeed)
+			throw new System.Exception ("HashWrapper for HashVarySeedSequence must support seed.");
+		this.wrapper = wrapper;
 		this.index = index;
 		Reset ();
 	}
 	public void Reset () {
 		seed = 0;
 	}
-	public int Next () {
-		int value = new MurmurHash (seed).GetHash (index);
+	public uint Next () {
+		uint value = wrapper.getHash (wrapper.getGenerator (seed), index);
 		seed++;
 		return value;
 	}
 	public string GetName () {
-		return string.Format ("MurmurHash3\n{0} number of seed #", index.Ordinal ());
-	}
-}
-
-public class PcgHashWrapper : RandomSequence {
-	private int index;
-	public PcgHashWrapper () {
-		Reset ();
-	}
-	public void Reset () {
-		index = 0;
-	}
-	public int Next () {
-		int value = PcgHash.Hash (index);
-		index++;
-		return value;
-	}
-	public string GetName () {
-		return "PcgHash\nnumbers #";
+		return string.Format ("{0}\n{1} number of seed #", wrapper.name, index.Ordinal ());
 	}
 }
-
-public class MD5HashWrapper : RandomSequence {
-	private MD5 hash;
-	private int index;
-	public MD5HashWrapper () {
-		hash = MD5.Create ();
-		Reset ();
-	}
-	public void Reset () {
-		index = 0;
-	}
-	public int Next () {
-		int value = BitConverter.ToInt32 (hash.ComputeHash (BitConverter.GetBytes (index)), 0);
-		index++;
-		return value;
-	}
-	public string GetName () {
-		return "MD5\nnumbers #";
-	}
-}
-
-public class LinearFunction : RandomSequence {
-	private int multiplier;
-	private int divisor;
-	private int index;
-	public LinearFunction (int multiplier, int divisor) {
-		this.multiplier = multiplier;
-		this.divisor = divisor;
-		Reset ();
-	}
-	public void Reset () {
-		index = 0;
-	}
-	public int Next () {
-		int value = index * multiplier / divisor;
-		index++;
-		return value;
-	}
-	public string GetName () {
-		return string.Format ("Linear function i * {0} / {1}\nnumbers #", multiplier, divisor);
-	}
-}

          
M Assets/Testing/RandomHashTester.cs.meta => Assets/Testing/RandomnessTester.cs.meta +0 -0

        
M Assets/UnityFrontend/NoiseTestMB.cs +5 -5
@@ 19,7 19,7 @@ public class TestFront {
 }
 
 public class NoiseTestMB : MonoBehaviour {
-	RandomHashTester tester;
+	RandomnessTester tester;
 	TestFront[] fronts;
 	Vector2 scroll;
 	bool showOptions = false;

          
@@ 35,7 35,7 @@ public class NoiseTestMB : MonoBehaviour
 	bool showStats = true;
 	
 	void Start () {
-		tester = new RandomHashTester ();
+		tester = new RandomnessTester ();
 		fronts = new TestFront[tester.tests.Count];
 
 		for (int i=0; i<fronts.Length; i++) {

          
@@ 44,7 44,7 @@ public class NoiseTestMB : MonoBehaviour
 	}
 
 	void Initialize (int testNr) {
-		RandomHashTest test = tester.tests[testNr];
+		RandomnessTest test = tester.tests[testNr];
 		TestFront front = new TestFront ();
 		fronts[testNr] = front;
 

          
@@ 54,7 54,7 @@ public class NoiseTestMB : MonoBehaviour
 		test.Reset ();
 		for (int j=front.noiseTexture.height-1; j>=0; j--) {
 			for (int i=0; i<front.noiseTexture.width; i++) {
-				float f = test.Function256 () / 255f;
+				float f = test.noiseSequence[i + j*256];
 				front.noiseTexture.SetPixel (i, j, new Color (f,f,f,1));
 			}
 		}

          
@@ 96,7 96,7 @@ public class NoiseTestMB : MonoBehaviour
 			if (!front.enabled)
 				continue;
 
-			RandomHashTest test = tester.tests[i];
+			RandomnessTest test = tester.tests[i];
 			top = vSpacing;
 			float height;