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;