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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiThreadedRng(uint seedU, uint seedV)
|
||||||
|
{
|
||||||
|
this.mW = seedU;
|
||||||
|
this.mZ = seedV;
|
||||||
|
this.StartProducerThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartProducerThreads()
|
||||||
|
{
|
||||||
|
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.producerRandom1 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.Writer, this.producerToken.Token)) {IsBackground = true};
|
this.producerRandomUniformDistributedDouble[0] = new Thread(() => this.RandomProducerUniformDistributedDouble(this.channelRandomUint.Reader, channelRandomUniformDistributedDouble.Writer, this.producerToken.Token)) {IsBackground = true};
|
||||||
this.producerRandom2 = new Thread(() => MultiThreadedRng.RandomProducer(this.rng, this.channelRandom.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.producerRandom1.Start();
|
this.producerRandomUniformDistributedDouble[1].Start();
|
||||||
this.producerRandom2.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,25 +109,78 @@ namespace FastRng
|
|||||||
await channelWriter.WriteAsync(buffer[n], cancellationToken);
|
await channelWriter.WriteAsync(buffer[n], cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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