Refactored to use distributions
This commit is contained in:
		
							parent
							
								
									90804bdf2b
								
							
						
					
					
						commit
						bc68dd0971
					
				@ -1,12 +1,17 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Diagnostics.CodeAnalysis;
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Runtime.CompilerServices;
 | 
					 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Channels;
 | 
					using System.Threading.Channels;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using FastRng.Distributions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FastRng
 | 
					namespace FastRng
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// This class uses the George Marsaglia's MWC algorithm. The algorithm's implementation based loosely on John D.
 | 
				
			||||||
 | 
					    /// Cook's (johndcook.com) implementation (https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation).
 | 
				
			||||||
 | 
					    /// Thanks John for your work.
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
    public sealed class MultiThreadedRng : IRandom
 | 
					    public sealed class MultiThreadedRng : IRandom
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        #if DEBUG
 | 
					        #if DEBUG
 | 
				
			||||||
@ -15,15 +20,23 @@ namespace FastRng
 | 
				
			|||||||
            private const int CAPACITY_RANDOM_NUMBERS_4_SOURCE = 16_000_000;
 | 
					            private const int CAPACITY_RANDOM_NUMBERS_4_SOURCE = 16_000_000;
 | 
				
			||||||
        #endif
 | 
					        #endif
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private static readonly object SYNC = new object();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private readonly System.Random rng = new System.Random();
 | 
					 | 
				
			||||||
        private readonly CancellationTokenSource producerToken = new CancellationTokenSource();
 | 
					        private readonly CancellationTokenSource producerToken = new CancellationTokenSource();
 | 
				
			||||||
 | 
					        private readonly object syncUintGenerators = new object();
 | 
				
			||||||
 | 
					        private readonly object syncUniformDistributedDoubleGenerators = new object();
 | 
				
			||||||
 | 
					        private readonly Thread[] producerRandomUint = new Thread[2];
 | 
				
			||||||
 | 
					        private readonly Thread[] producerRandomUniformDistributedDouble = new Thread[2];
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private readonly Thread producerRandom1;
 | 
					        private uint mW;
 | 
				
			||||||
        private readonly Thread producerRandom2;
 | 
					        private uint mZ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Channel<double> channelRandom = Channel.CreateBounded<double>(new BoundedChannelOptions(CAPACITY_RANDOM_NUMBERS_4_SOURCE)
 | 
					        private readonly Channel<uint> channelRandomUint = Channel.CreateBounded<uint>(new BoundedChannelOptions(CAPACITY_RANDOM_NUMBERS_4_SOURCE)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            FullMode = BoundedChannelFullMode.Wait,
 | 
				
			||||||
 | 
					            SingleReader = false,
 | 
				
			||||||
 | 
					            SingleWriter = false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private readonly Channel<double> channelRandomUniformDistributedDouble = Channel.CreateBounded<double>(new BoundedChannelOptions(CAPACITY_RANDOM_NUMBERS_4_SOURCE)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            FullMode = BoundedChannelFullMode.Wait,
 | 
					            FullMode = BoundedChannelFullMode.Wait,
 | 
				
			||||||
            SingleReader = false,
 | 
					            SingleReader = false,
 | 
				
			||||||
@ -34,61 +47,61 @@ namespace FastRng
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public MultiThreadedRng()
 | 
					        public MultiThreadedRng()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.producerRandom1 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
					            //
 | 
				
			||||||
            this.producerRandom2 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
					            // Initialize the mW and mZ by using
 | 
				
			||||||
            this.producerRandom1.Start();
 | 
					            // the system's time.
 | 
				
			||||||
            this.producerRandom2.Start();
 | 
					            //
 | 
				
			||||||
 | 
					            var now = DateTime.Now;
 | 
				
			||||||
 | 
					            var ticks = now.Ticks;
 | 
				
			||||||
 | 
					            this.mW = (uint) (ticks >> 16);
 | 
				
			||||||
 | 
					            this.mZ = (uint) (ticks % 4294967296);
 | 
				
			||||||
 | 
					            this.StartProducerThreads();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MultiThreadedRng(int seed)
 | 
					        public MultiThreadedRng(uint seedU)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.rng = new Random(seed);
 | 
					            this.mW = seedU;
 | 
				
			||||||
 | 
					            this.mZ = 362436069;
 | 
				
			||||||
 | 
					            this.StartProducerThreads();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            this.producerRandom1 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
					        public MultiThreadedRng(uint seedU, uint seedV)
 | 
				
			||||||
            this.producerRandom2 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
					        {
 | 
				
			||||||
 | 
					            this.mW = seedU;
 | 
				
			||||||
 | 
					            this.mZ = seedV;
 | 
				
			||||||
 | 
					            this.StartProducerThreads();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.producerRandom1.Start();
 | 
					        private void StartProducerThreads()
 | 
				
			||||||
            this.producerRandom2.Start();
 | 
					        {
 | 
				
			||||||
 | 
					            this.producerRandomUint[0] = new Thread(() => this.RandomProducerUint(this.channelRandomUint.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
				
			||||||
 | 
					            this.producerRandomUint[1] = new Thread(() => this.RandomProducerUint(this.channelRandomUint.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
				
			||||||
 | 
					            this.producerRandomUint[0].Start();
 | 
				
			||||||
 | 
					            this.producerRandomUint[1].Start();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            this.producerRandomUniformDistributedDouble[0] = new Thread(() => this.RandomProducerUniformDistributedDouble(this.channelRandomUint.Reader, channelRandomUniformDistributedDouble.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
				
			||||||
 | 
					            this.producerRandomUniformDistributedDouble[1] = new Thread(() => this.RandomProducerUniformDistributedDouble(this.channelRandomUint.Reader, channelRandomUniformDistributedDouble.Writer, this.producerToken.Token)) {IsBackground = true};
 | 
				
			||||||
 | 
					            this.producerRandomUniformDistributedDouble[0].Start();
 | 
				
			||||||
 | 
					            this.producerRandomUniformDistributedDouble[1].Start();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Producers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [ExcludeFromCodeCoverage]
 | 
					        [ExcludeFromCodeCoverage]
 | 
				
			||||||
        private static async void RandomProducer(System.Random random, ChannelWriter<double> channelWriter, CancellationToken cancellationToken)
 | 
					        private async void RandomProducerUint(ChannelWriter<uint> channelWriter, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var buffer = new uint[CAPACITY_RANDOM_NUMBERS_4_SOURCE];
 | 
				
			||||||
            while (!cancellationToken.IsCancellationRequested)
 | 
					            while (!cancellationToken.IsCancellationRequested)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                //
 | 
					                lock (syncUintGenerators)
 | 
				
			||||||
                // We using double as basis for anything. That's what .NET does internally as well, cf. https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Private.CoreLib/src/System/Random.cs.
 | 
					 | 
				
			||||||
                // random.NextDouble() returns Sample(). Next(min, max) uses GetSampleForLargeRange().
 | 
					 | 
				
			||||||
                // Thus, we re-implement GetSampleForLargeRange() and use its numbers as source for everything. 
 | 
					 | 
				
			||||||
                //
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                var buffer = new double[CAPACITY_RANDOM_NUMBERS_4_SOURCE];
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                //
 | 
					 | 
				
			||||||
                // Random is not thread-safe!
 | 
					 | 
				
			||||||
                // Because we using two threads, we ensure that one threads generates
 | 
					 | 
				
			||||||
                // next bag of numbers while the other pumps its numbers into the channel.
 | 
					 | 
				
			||||||
                //
 | 
					 | 
				
			||||||
                lock (SYNC)
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    for (var n = 0; n < buffer.Length && !cancellationToken.IsCancellationRequested; n++)
 | 
					                    for (var n = 0; n < buffer.Length && !cancellationToken.IsCancellationRequested; n++)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        #region Re-implementation of GetSampleForLargeRange() method of .NET
 | 
					                        this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16);
 | 
				
			||||||
 | 
					                        this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16);
 | 
				
			||||||
                        var result = random.Next(); // Notice: random.Next() is identical to InternalSample()
 | 
					                        buffer[n] = (this.mZ << 16) + this.mW;
 | 
				
			||||||
                        var negative = random.Next() % 2 == 0; // Notice: random.Next() is identical to InternalSample()
 | 
					 | 
				
			||||||
                        if (negative)
 | 
					 | 
				
			||||||
                            result = -result;
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                        double d = result;
 | 
					 | 
				
			||||||
                        d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
 | 
					 | 
				
			||||||
                        d /= 2 * (uint)int.MaxValue - 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        #endregion
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                        buffer[n] = d;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -97,24 +110,77 @@ namespace FastRng
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        [ExcludeFromCodeCoverage]
 | 
				
			||||||
 | 
					        private async void RandomProducerUniformDistributedDouble(ChannelReader<uint> channelReaderUint, ChannelWriter<double> channelWriter, CancellationToken cancellationToken)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var buffer = new double[CAPACITY_RANDOM_NUMBERS_4_SOURCE];
 | 
				
			||||||
 | 
					            var randomUint = new uint[CAPACITY_RANDOM_NUMBERS_4_SOURCE];
 | 
				
			||||||
 | 
					            while (!cancellationToken.IsCancellationRequested)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                for (var n = 0; n < randomUint.Length; n++)
 | 
				
			||||||
 | 
					                    randomUint[n] = await channelReaderUint.ReadAsync(cancellationToken);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                lock (syncUniformDistributedDoubleGenerators)
 | 
				
			||||||
 | 
					                    for (var n = 0; n < buffer.Length && !cancellationToken.IsCancellationRequested; n++)
 | 
				
			||||||
 | 
					                        buffer[n] = (randomUint[n] + 1.0) * 2.328306435454494e-10; // 2.328 => 1/(2^32 + 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (var n = 0; n < buffer.Length && !cancellationToken.IsCancellationRequested; n++) 
 | 
				
			||||||
 | 
					                    await channelWriter.WriteAsync(buffer[n], cancellationToken);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region Implementing interface
 | 
					        #region Implementing interface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<uint> NextNumber(uint rangeStart, uint rangeEnd, CancellationToken cancel = default(CancellationToken))
 | 
					        public async Task<double> GetUniformDouble(CancellationToken cancel = default) => await this.channelRandomUniformDistributedDouble.Reader.ReadAsync(cancel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<uint> NextNumber(uint rangeStart, uint rangeEnd, IDistribution distribution, CancellationToken cancel = default)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (rangeStart > rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var tmp = rangeStart;
 | 
				
			||||||
 | 
					                rangeStart = rangeEnd;
 | 
				
			||||||
 | 
					                rangeEnd = tmp;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            var range = rangeEnd - rangeStart;
 | 
					            var range = rangeEnd - rangeStart;
 | 
				
			||||||
            return (uint) ((await this.channelRandom.Reader.ReadAsync(cancel) * range) + rangeStart);
 | 
					            distribution.Random = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var distributedValue = await distribution.GetDistributedValue(cancel);
 | 
				
			||||||
 | 
					            return (uint) ((distributedValue * range) + rangeStart);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, CancellationToken cancel = default(CancellationToken))
 | 
					        public async Task<ulong> NextNumber(ulong rangeStart, ulong rangeEnd, IDistribution distribution, CancellationToken cancel = default(CancellationToken))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (rangeStart > rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var tmp = rangeStart;
 | 
				
			||||||
 | 
					                rangeStart = rangeEnd;
 | 
				
			||||||
 | 
					                rangeEnd = tmp;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            var range = rangeEnd - rangeStart;
 | 
					            var range = rangeEnd - rangeStart;
 | 
				
			||||||
            return (ulong) ((await this.channelRandom.Reader.ReadAsync(cancel) * range) + rangeStart);
 | 
					            distribution.Random = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var distributedValue = await distribution.GetDistributedValue(cancel);
 | 
				
			||||||
 | 
					            return (ulong) ((distributedValue * range) + rangeStart);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<float> NextNumber(float rangeStart, float rangeEnd, CancellationToken cancel = default(CancellationToken))
 | 
					        public async Task<float> NextNumber(float rangeStart, float rangeEnd, IDistribution distribution, CancellationToken cancel = default(CancellationToken))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (rangeStart > rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var tmp = rangeStart;
 | 
				
			||||||
 | 
					                rangeStart = rangeEnd;
 | 
				
			||||||
 | 
					                rangeEnd = tmp;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            var range = rangeEnd - rangeStart;
 | 
					            var range = rangeEnd - rangeStart;
 | 
				
			||||||
            return (float) ((await this.channelRandom.Reader.ReadAsync(cancel) * range) + rangeStart);
 | 
					            distribution.Random = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var distributedValue = await distribution.GetDistributedValue(cancel);
 | 
				
			||||||
 | 
					            return (float) ((distributedValue * range) + rangeStart);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void StopProducer() => this.producerToken.Cancel();
 | 
					        public void StopProducer() => this.producerToken.Cancel();
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using FastRng;
 | 
					using FastRng;
 | 
				
			||||||
 | 
					using FastRng.Distributions;
 | 
				
			||||||
using NUnit.Framework;
 | 
					using NUnit.Framework;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FastRngTests
 | 
					namespace FastRngTests
 | 
				
			||||||
@ -17,8 +18,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange01Uint()
 | 
					        public async Task TestRange01Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            for (uint n = 0; n < 1_000_000; n++)
 | 
					            for (uint n = 0; n < 1_000_000; n++)
 | 
				
			||||||
                Assert.That(await rng.NextNumber(n, 100_000 + n), Is.InRange(n, 100_000 + n));
 | 
					                Assert.That(await rng.NextNumber(n, 100_000 + n, dist), Is.InRange(n, 100_000 + n));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -26,8 +28,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange01Ulong()
 | 
					        public async Task TestRange01Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            for (ulong n = 0; n < 1_000_000; n++)
 | 
					            for (ulong n = 0; n < 1_000_000; n++)
 | 
				
			||||||
                Assert.That(await rng.NextNumber(n, 100_000 + n), Is.InRange(n, 100_000 + n));
 | 
					                Assert.That(await rng.NextNumber(n, 100_000 + n, dist), Is.InRange(n, 100_000 + n));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -35,8 +38,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange01Float()
 | 
					        public async Task TestRange01Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            for (var n = 0f; n < 1e6f; n++)
 | 
					            for (var n = 0f; n < 1e6f; n++)
 | 
				
			||||||
                Assert.That(await rng.NextNumber(n, 100_000 + n), Is.InRange(n, 100_000 + n));
 | 
					                Assert.That(await rng.NextNumber(n, 100_000 + n, dist), Is.InRange(n, 100_000 + n));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -44,9 +48,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange02Uint()
 | 
					        public async Task TestRange02Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5, 5), Is.EqualTo(5));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0, 0), Is.EqualTo(0));
 | 
					            Assert.That(await rng.NextNumber(5, 5, dist), Is.EqualTo(5));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3_000_000_000, 3_000_000_000), Is.EqualTo(3_000_000_000));
 | 
					            Assert.That(await rng.NextNumber(0, 0, dist), Is.EqualTo(0));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3_000_000_000, 3_000_000_000, dist), Is.EqualTo(3_000_000_000));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -54,9 +59,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange02Ulong()
 | 
					        public async Task TestRange02Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5UL, 5), Is.EqualTo(5));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0UL, 0), Is.EqualTo(0));
 | 
					            Assert.That(await rng.NextNumber(5UL, 5, dist), Is.EqualTo(5));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3_000_000_000UL, 3_000_000_000), Is.EqualTo(3_000_000_000));
 | 
					            Assert.That(await rng.NextNumber(0UL, 0, dist), Is.EqualTo(0));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3_000_000_000UL, 3_000_000_000, dist), Is.EqualTo(3_000_000_000));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -64,9 +70,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange02Float()
 | 
					        public async Task TestRange02Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5f, 5f), Is.EqualTo(5));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0f, 0f), Is.EqualTo(0));
 | 
					            Assert.That(await rng.NextNumber(5f, 5f, dist), Is.EqualTo(5));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3e9f, 3e9f), Is.EqualTo(3e9f));
 | 
					            Assert.That(await rng.NextNumber(0f, 0f, dist), Is.EqualTo(0));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3e9f, 3e9f, dist), Is.EqualTo(3e9f));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -74,9 +81,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange03Uint()
 | 
					        public async Task TestRange03Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5, 6), Is.InRange(5, 6));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0, 1), Is.InRange(0, 1));
 | 
					            Assert.That(await rng.NextNumber(5, 6, dist), Is.InRange(5, 6));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3_000_000_000, 3_000_000_002), Is.InRange(3_000_000_000, 3_000_000_002));
 | 
					            Assert.That(await rng.NextNumber(0, 1, dist), Is.InRange(0, 1));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3_000_000_000, 3_000_000_002, dist), Is.InRange(3_000_000_000, 3_000_000_002));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -84,9 +92,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange03Ulong()
 | 
					        public async Task TestRange03Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5UL, 6), Is.InRange(5, 6));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0UL, 1), Is.InRange(0, 1));
 | 
					            Assert.That(await rng.NextNumber(5UL, 6, dist), Is.InRange(5, 6));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3_000_000_000UL, 3_000_000_002), Is.InRange(3_000_000_000, 3_000_000_002));
 | 
					            Assert.That(await rng.NextNumber(0UL, 1, dist), Is.InRange(0, 1));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3_000_000_000UL, 3_000_000_002, dist), Is.InRange(3_000_000_000, 3_000_000_002));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -94,9 +103,10 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange03Float()
 | 
					        public async Task TestRange03Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(5f, 6), Is.InRange(5, 6));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(0f, 1), Is.InRange(0, 1));
 | 
					            Assert.That(await rng.NextNumber(5f, 6, dist), Is.InRange(5, 6));
 | 
				
			||||||
            Assert.That(await rng.NextNumber(3e9f, 3e9f+2), Is.InRange(3e9f, 3e9f+2));
 | 
					            Assert.That(await rng.NextNumber(0f, 1, dist), Is.InRange(0, 1));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(3e9f, 3e9f+2, dist), Is.InRange(3e9f, 3e9f+2));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -104,8 +114,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange04Uint()
 | 
					        public async Task TestRange04Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(10, 1), Is.InRange(1, 10));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(20, 1), Is.InRange(1, 20));
 | 
					            Assert.That(await rng.NextNumber(10, 1, dist), Is.InRange(1, 10));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(20, 1, dist), Is.InRange(1, 20));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -113,8 +124,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange04Ulong()
 | 
					        public async Task TestRange04Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(10UL, 1), Is.InRange(1, 10));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(20UL, 1), Is.InRange(1, 20));
 | 
					            Assert.That(await rng.NextNumber(10UL, 1, dist), Is.InRange(1, 10));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(20UL, 1, dist), Is.InRange(1, 20));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -122,8 +134,9 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange04Float()
 | 
					        public async Task TestRange04Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Assert.That(await rng.NextNumber(10f, 1), Is.InRange(1, 10));
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            Assert.That(await rng.NextNumber(20f, 1), Is.InRange(1, 20));
 | 
					            Assert.That(await rng.NextNumber(10f, 1, dist), Is.InRange(1, 10));
 | 
				
			||||||
 | 
					            Assert.That(await rng.NextNumber(20f, 1, dist), Is.InRange(1, 20));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
@ -131,10 +144,11 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange05Uint()
 | 
					        public async Task TestRange05Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0, 100)]++;
 | 
					                distribution[await rng.NextNumber(0, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            for (var n = 0; n < distribution.Length - 1; n++)
 | 
					            for (var n = 0; n < distribution.Length - 1; n++)
 | 
				
			||||||
                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
					                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
				
			||||||
@ -145,10 +159,11 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange05Ulong()
 | 
					        public async Task TestRange05Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0UL, 100)]++;
 | 
					                distribution[await rng.NextNumber(0UL, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            for (var n = 0; n < distribution.Length - 1; n++)
 | 
					            for (var n = 0; n < distribution.Length - 1; n++)
 | 
				
			||||||
                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
					                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
				
			||||||
@ -159,10 +174,11 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestRange05Float()
 | 
					        public async Task TestRange05Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f))]++;
 | 
					                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f, dist))]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            for (var n = 0; n < distribution.Length - 1; n++)
 | 
					            for (var n = 0; n < distribution.Length - 1; n++)
 | 
				
			||||||
                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
					                Assert.That(distribution[n], Is.GreaterThan(0));
 | 
				
			||||||
@ -172,72 +188,78 @@ namespace FastRngTests
 | 
				
			|||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestDistribution001Uint()
 | 
					        public async Task TestDistribution001Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0, 100)]++;
 | 
					                distribution[await rng.NextNumber(0, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 600));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestDistribution001Ulong()
 | 
					        public async Task TestDistribution001Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0UL, 100)]++;
 | 
					                distribution[await rng.NextNumber(0UL, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 600));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
        [Category(TestCategories.NORMAL)]
 | 
					        [Category(TestCategories.NORMAL)]
 | 
				
			||||||
        public async Task TestDistribution001Float()
 | 
					        public async Task TestDistribution001Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 1_000_000;
 | 
					            var runs = 1_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f))]++;
 | 
					                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f, dist))]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 600));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
        [Category(TestCategories.LONG_RUNNING)]
 | 
					        [Category(TestCategories.LONG_RUNNING)]
 | 
				
			||||||
        public async Task TestDistribution002Uint()
 | 
					        public async Task TestDistribution002Uint()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 100_000_000;
 | 
					            var runs = 100_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0, 100)]++;
 | 
					                distribution[await rng.NextNumber(0, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 6_000));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
        [Category(TestCategories.LONG_RUNNING)]
 | 
					        [Category(TestCategories.LONG_RUNNING)]
 | 
				
			||||||
        public async Task TestDistribution002Ulong()
 | 
					        public async Task TestDistribution002Ulong()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 100_000_000;
 | 
					            var runs = 100_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[await rng.NextNumber(0UL, 100)]++;
 | 
					                distribution[await rng.NextNumber(0UL, 100, dist)]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 6_000));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
        [Category(TestCategories.LONG_RUNNING)]
 | 
					        [Category(TestCategories.LONG_RUNNING)]
 | 
				
			||||||
        public async Task TestDistribution002Float()
 | 
					        public async Task TestDistribution002Float()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            var dist = new Uniform();
 | 
				
			||||||
            var distribution = new uint[101];
 | 
					            var distribution = new uint[101];
 | 
				
			||||||
            var runs = 100_000_000;
 | 
					            var runs = 100_000_000;
 | 
				
			||||||
            for (var n = 0; n < runs; n++)
 | 
					            for (var n = 0; n < runs; n++)
 | 
				
			||||||
                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f))]++;
 | 
					                distribution[(uint)MathF.Floor(await rng.NextNumber(0f, 100f, dist))]++;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Assert.That(distribution[..100].Max() - distribution[..100].Min(), Is.InRange(0, 600));
 | 
					            Assert.That(distribution[..^1].Max() - distribution[..^1].Min(), Is.InRange(0, 6_000));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user