public void AffineHash_Constructor_Rejects_Invalid_Arguments()
        {
            var cases = new[]
            {
                new { scale = 0L, shift = 0L, expected = typeof(ArgumentOutOfRangeException) },
                new { scale = 1L, shift = -1L, expected = typeof(ArgumentOutOfRangeException) },
                new { scale = 1L << 32, shift = 0L, expected = typeof(ArgumentOutOfRangeException) },
                new { scale = 1L, shift = 1L << 32, expected = typeof(ArgumentOutOfRangeException) },
                new { scale = 2L, shift = 0L, expected = typeof(ArgumentException) },
            };

            for (int i = 0; i < cases.Length; i++)
            {
                var c = cases[i];
                try
                {
                    AffineHash target = new AffineHash(c.scale, c.shift);
                    Assert.Fail("index:{0}, target:{1}", i, target);
                }
                catch (Exception ex)
                {
                    Assert.AreEqual(c.expected, ex.GetType(), "index: {0}", i);
                }
            }
        }
        /// <summary>
        /// Gets the hash method delegate for the specified arguments and return type.
        /// </summary>
        /// <param name="target">The target hash instance.</param>
        /// <param name="args">The argument types of the hash method.</param>
        /// <returns>A delegate that will call the hash method on the target.</returns>
        private static Delegate HashDelegate(AffineHash target, params Type[] args)
        {
            Type[] delegateTypeArgs = new Type[args.Length + 1];
            args.CopyTo(delegateTypeArgs, 0);
            delegateTypeArgs[args.Length] = typeof(int);
            Type delegateType = null;

            switch (args.Length)
            {
            case 1:
                delegateType = typeof(Func <,>);
                break;

            case 2:
                delegateType = typeof(Func <, ,>);
                break;

            case 3:
                delegateType = typeof(Func <, , ,>);
                break;
            }

            delegateType = delegateType.MakeGenericType(delegateTypeArgs);
            return(typeof(AffineHash).GetMethod(nameof(AffineHash.GetHashCode), args).CreateDelegate(delegateType, target));
        }
        public void AffineHash_Hash_Distributes_Byte_Array()
        {
            Random               random = AffineHashTests.Random();
            AffineHash           target = new AffineHash(random);
            IEnumerable <byte[]> values = AffineHashTests.RandomByteArrays(random, 101, 10000);

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_Byte()
        {
            Random             random = AffineHashTests.Random();
            AffineHash         target = new AffineHash(random);
            IEnumerable <byte> values = Enumerable.Range(byte.MinValue, byte.MaxValue - byte.MinValue + 1)
                                        .Select(n => (byte)n);

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_DateTimeOffset()
        {
            Random     random = AffineHashTests.Random();
            AffineHash target = new AffineHash(random);
            IEnumerable <DateTimeOffset> values = AffineHashTests.RandomByteArrays(random, sizeof(ulong), 10000)
                                                  .Select(b => new DateTimeOffset((long)(BitConverter.ToUInt64(b, 0) % (ulong)DateTime.MaxValue.Ticks), TimeSpan.FromMinutes(random.Next(-840, 840))));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_UInt64()
        {
            Random              random = AffineHashTests.Random();
            AffineHash          target = new AffineHash(random);
            IEnumerable <ulong> values = AffineHashTests.RandomByteArrays(random, sizeof(ulong), 10000)
                                         .Select(b => BitConverter.ToUInt64(b, 0));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_UInt16()
        {
            Random               random = AffineHashTests.Random();
            AffineHash           target = new AffineHash(random);
            IEnumerable <ushort> values = Enumerable.Range(ushort.MinValue, ushort.MaxValue - ushort.MinValue + 1)
                                          .Select(n => (ushort)n);

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_String()
        {
            Random               random = AffineHashTests.Random();
            AffineHash           target = new AffineHash(random);
            IEnumerable <string> values = AffineHashTests.RandomCharArrays(random, 101, 10000)
                                          .Select(b => new string(b));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_Single()
        {
            Random              random = AffineHashTests.Random();
            AffineHash          target = new AffineHash(random);
            IEnumerable <float> values = AffineHashTests.RandomByteArrays(random, sizeof(float), 10000)
                                         .Select(b => BitConverter.ToSingle(b, 0));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_Guid()
        {
            Random             random = AffineHashTests.Random();
            AffineHash         target = new AffineHash(random);
            IEnumerable <Guid> values = AffineHashTests.RandomByteArrays(random, 16, 10000)
                                        .Select(b => new Guid(b));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_Decimal()
        {
            Random                random = AffineHashTests.Random();
            AffineHash            target = new AffineHash(random);
            IEnumerable <decimal> values = Enumerable.Range(0, 10000)
                                           .Select(n => new decimal(random.Next(), random.Next(), random.Next(), random.NextDouble() > 0.5D, (byte)random.Next(29)));

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Distributes_Char()
        {
            Random             random = AffineHashTests.Random();
            AffineHash         target = new AffineHash(random);
            IEnumerable <char> values = Enumerable.Range(char.MinValue, char.MaxValue - char.MinValue + 1)
                                        .Select(n => (char)n);

            AffineHashTests.AssertHashDistribution(target, values);
        }
        public void AffineHash_Hash_Matches_Identical_String_Segments()
        {
            Random     random   = AffineHashTests.Random();
            AffineHash target   = new AffineHash(random);
            string     value    = "TestTest";
            int        expected = target.GetHashCode(value, 0, 4);
            int        actual   = target.GetHashCode(value, 4, 4);

            Assert.AreEqual(expected, actual);
        }
        public void AffineHash_Hash_Matches_Identical_Byte_Array_Segments()
        {
            Random     random = AffineHashTests.Random();
            AffineHash target = new AffineHash(random);

            byte[] value    = { 0, 1, 2, 3, 0, 1, 2, 3 };
            int    expected = target.GetHashCode(value, 0, 4);
            int    actual   = target.GetHashCode(value, 4, 4);

            Assert.AreEqual(expected, actual);
        }
        public void AffineHash_Hash_Matches_Identical_Char_Array_Segments()
        {
            Random     random = AffineHashTests.Random();
            AffineHash target = new AffineHash(random);

            char[] value    = { 'T', 'e', 's', 't', 'T', 'e', 's', 't' };
            int    expected = target.GetHashCode(value, 0, 4);
            int    actual   = target.GetHashCode(value, 4, 4);

            Assert.AreEqual(expected, actual);
        }
        public void AffineHash_Hash_Measure_Avalanche()
        {
            Random random = new Random();
            var    cases  = new[]
            {
                new { name = "zeroes", source = Enumerable.Repeat((byte)0x00, 100).ToArray() },
                new { name = "ones", source = Enumerable.Repeat((byte)0xFF, 100).ToArray() },
                new { name = "alternating-right", source = Enumerable.Repeat((byte)0x55, 100).ToArray() },
                new { name = "alternating-left", source = Enumerable.Repeat((byte)0xAA, 100).ToArray() },
                new { name = "random", source = Enumerable.Repeat(default(byte), 100).Select(b => (byte)random.Next()).ToArray() },
            };
            Avalanche avalanche = new Avalanche();

            this.TestContext.WriteLine("power,predictability,scale,shift");
            for (int trial = 0; trial < 10000; trial++)
            {
                AffineHash target = new AffineHash(random);
                avalanche.Reset(target);
                bool strong = true;
                bool weak   = false;
                foreach (var c in cases)
                {
                    avalanche.Next(c.source);
                    double predictability = avalanche.Predictability();
                    if (predictability > 0.1)
                    {
                        strong = false;
                    }

                    if (predictability > 0.2)
                    {
                        weak = true;
                    }
                }

                if (strong)
                {
                    double predictability = avalanche.Finish();
                    this.TestContext.WriteLine("strong,{0},{1},{2}", predictability, target.Scale, target.Shift);
                }

                if (weak)
                {
                    double predictability = avalanche.Finish();
                    this.TestContext.WriteLine("weak,{0},{1},{2}", predictability, target.Scale, target.Shift);
                }
            }
        }
        public void AffineHash_Hash_Rejects_Invalid_Arguments()
        {
            var cases = new[]
            {
                new { arguments = new object[] { null }, types = new Type[] { typeof(byte[]) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { null }, types = new Type[] { typeof(char[]) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { null }, types = new Type[] { typeof(string) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { null, 0, 0 }, types = new Type[] { typeof(byte[]), typeof(int), typeof(int) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { null, 0, 0 }, types = new Type[] { typeof(char[]), typeof(int), typeof(int) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { null, 0, 0 }, types = new Type[] { typeof(string), typeof(int), typeof(int) }, expected = typeof(ArgumentNullException) },
                new { arguments = new object[] { new byte[10], -1, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new byte[10], 10, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new byte[10], 5, -1 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new byte[10], 5, 6 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new char[10], -1, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new char[10], 10, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new char[10], 5, -1 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { new char[10], 5, 6 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { "1234567890", -1, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { "1234567890", 10, 5 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { "1234567890", 5, -1 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
                new { arguments = new object[] { "1234567890", 5, 6 }, types = (Type[])null, expected = typeof(ArgumentOutOfRangeException) },
            };
            AffineHash target = new AffineHash(1L, 0L);

            for (int i = 0; i < cases.Length; i++)
            {
                var      c      = cases[i];
                Type[]   types  = c.types ?? c.arguments.Select(a => a.GetType()).ToArray();
                Delegate method = AffineHashTests.HashDelegate(target, types);
                try
                {
                    object result = method.DynamicInvoke(c.arguments);
                    Assert.Fail("index: {0}, return: {1}", i, result);
                }
                catch (TargetInvocationException ex)
                {
                    Assert.AreEqual(c.expected, ex.InnerException.GetType(), "index: {0}", i);
                }
                catch (Exception ex)
                {
                    Assert.Fail("index: {0}, exception: {1}", i, ex);
                }
            }
        }
        /// <summary>
        /// Asserts that the distribution of hash values is uniform for a given set of data.
        /// </summary>
        /// <typeparam name="T">The type of data to hash.</typeparam>
        /// <param name="target">The target hash algorithm.</param>
        /// <param name="values">The values to hash.</param>
        private static void AssertHashDistribution <T>(AffineHash target, IEnumerable <T> values)
        {
            Distribution        distribution = new Distribution();
            Dictionary <int, T> hashes       = new Dictionary <int, T>();
            Func <T, int>       method       = (Func <T, int>)AffineHashTests.HashDelegate(target, typeof(T));

            foreach (T value in values)
            {
                int hash = method(value);
                distribution.Observe(hash);
                if (hashes.ContainsKey(hash))
                {
                    Assert.Fail("scale:{0}, shift:{1}, hash:{2}, original:{3}, collision:{4}", target.Scale, target.Shift, hash, hashes[hash], value);
                }
                else
                {
                    hashes.Add(hash, value);
                }
            }

            double ks = distribution.Finish();

            Assert.IsTrue(ks < 0.1D, "scale:{0}, shift:{1}, ks:{2}", target.Scale, target.Shift, ks);
        }
 /// <summary>
 /// Resets the hash algorithm.
 /// </summary>
 /// <param name="candidate">The new candidate algorithm.</param>
 public void Reset(AffineHash candidate)
 {
     this.hash       = candidate;
     this.grandLimit = 0;
     this.grandTotal = 0;
 }