Added possibility to get integers out of the RNG
This commit is contained in:
parent
f9ea837f68
commit
b2d8e2b5ed
@ -18,4 +18,29 @@ public interface IRandom<TNum> : IDisposable where TNum : IFloatingPointIeee754<
|
|||||||
/// by using multiple threads at the same time.
|
/// by using multiple threads at the same time.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public TNum GetUniform(CancellationToken cancel = default);
|
public TNum GetUniform(CancellationToken cancel = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a uniform distributed pseudo-random number from the interval [0, max).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is thread-safe. You can consume numbers from the same generator
|
||||||
|
/// by using multiple threads at the same time.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="max">The maximum value (exclusive). The max value returned will be max - 1.</param>
|
||||||
|
/// <param name="cancel">The cancellation token.</param>
|
||||||
|
/// <returns>A pseudo-random number from the interval [0, max).</returns>
|
||||||
|
public int GetUniformInt(int max, CancellationToken cancel = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a uniform distributed pseudo-random number from the interval [min, max).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is thread-safe. You can consume numbers from the same generator
|
||||||
|
/// by using multiple threads at the same time.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="min">The minimum value (inclusive).</param>
|
||||||
|
/// <param name="max">The maximum value (exclusive). The max value returned will be max - 1.</param>
|
||||||
|
/// <param name="cancel">The cancellation token.</param>
|
||||||
|
/// <returns>A pseudo-random number from the interval [min, max).</returns>
|
||||||
|
public int GetUniformInt(int min, int max, CancellationToken cancel = default);
|
||||||
}
|
}
|
@ -165,6 +165,20 @@ public sealed class MultiChannelRng<TNum> : IRandom<TNum>, IDisposable where TNu
|
|||||||
return valueTask.AsTask().Result;
|
return valueTask.AsTask().Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int GetUniformInt(int max, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var valueTask = this.channelIntegers.Reader.ReadAsync(cancel);
|
||||||
|
return (int) (valueTask.AsTask().Result % (uint) max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int GetUniformInt(int min, int max, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var valueTask = this.channelIntegers.Reader.ReadAsync(cancel);
|
||||||
|
return (int) (valueTask.AsTask().Result % (uint) (max - min) + (uint) min);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -61,6 +61,8 @@ public sealed class MultiThreadedRng<TNum> : IRandom<TNum>, IDisposable where TN
|
|||||||
|
|
||||||
// The uniform float producer thread:
|
// The uniform float producer thread:
|
||||||
private Thread producerRandomUniformDistributedFloat;
|
private Thread producerRandomUniformDistributedFloat;
|
||||||
|
|
||||||
|
private readonly UIntChannelProducer independentUIntProducer = new();
|
||||||
|
|
||||||
// Variable w and z for the uint generator. Both get used
|
// Variable w and z for the uint generator. Both get used
|
||||||
// as seeding variable as well (cf. constructors)
|
// as seeding variable as well (cf. constructors)
|
||||||
@ -312,6 +314,20 @@ public sealed class MultiThreadedRng<TNum> : IRandom<TNum>, IDisposable where TN
|
|||||||
//
|
//
|
||||||
return this.currentBuffer[myPointer];
|
return this.currentBuffer[myPointer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int GetUniformInt(int max, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var valueTask = this.independentUIntProducer.GetNextAsync(cancel);
|
||||||
|
return (int) (valueTask.AsTask().Result % (uint) max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int GetUniformInt(int min, int max, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var valueTask = this.independentUIntProducer.GetNextAsync(cancel);
|
||||||
|
return (int) (valueTask.AsTask().Result % (uint) (max - min) + (uint) min);
|
||||||
|
}
|
||||||
|
|
||||||
private void StopProducer() => this.producerTokenSource.Cancel();
|
private void StopProducer() => this.producerTokenSource.Cancel();
|
||||||
|
|
||||||
@ -320,7 +336,11 @@ public sealed class MultiThreadedRng<TNum> : IRandom<TNum>, IDisposable where TN
|
|||||||
/// when it is no longer needed. Otherwise, the background threads
|
/// when it is no longer needed. Otherwise, the background threads
|
||||||
/// are still running.
|
/// are still running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose() => this.StopProducer();
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.independentUIntProducer.Dispose();
|
||||||
|
this.StopProducer();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
121
FastRng/UIntChannelProducer.cs
Normal file
121
FastRng/UIntChannelProducer.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FastRng;
|
||||||
|
|
||||||
|
internal sealed class UIntChannelProducer : IDisposable
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
private const int BUFFER_SIZE = 1_000_000;
|
||||||
|
#else
|
||||||
|
private const int BUFFER_SIZE = 1_000_000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private readonly Channel<uint> channelIntegers = Channel.CreateBounded<uint>(new BoundedChannelOptions(capacity: BUFFER_SIZE * 2) { FullMode = BoundedChannelFullMode.Wait, SingleWriter = true, SingleReader = true });
|
||||||
|
|
||||||
|
// Gets used to stop the producer thread:
|
||||||
|
private readonly CancellationTokenSource producerTokenSource = new();
|
||||||
|
|
||||||
|
// The uint producer thread:
|
||||||
|
private Thread producerRandomUint;
|
||||||
|
|
||||||
|
// Variable w and z for the uint generator. Both get used
|
||||||
|
// as seeding variable as well (cf. constructors)
|
||||||
|
private uint mW;
|
||||||
|
private uint mZ;
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public UIntChannelProducer()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Initialize the mW and mZ by using
|
||||||
|
// the system's time.
|
||||||
|
//
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var ticks = now.Ticks;
|
||||||
|
this.mW = (uint) (ticks >> 16);
|
||||||
|
this.mZ = (uint) (ticks % 4_294_967_296);
|
||||||
|
this.StartProducerThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a multithreaded random number generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A multi-threaded random number generator created by this constructor is
|
||||||
|
/// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/>
|
||||||
|
///
|
||||||
|
/// <b>Please note:</b> Although the number generator and all distributions are deterministic,
|
||||||
|
/// the behavior of the consuming application might be non-deterministic. This is possible if
|
||||||
|
/// the application with multiple threads consumes the numbers. The scheduling of the threads
|
||||||
|
/// is up to the operating system and might not be predictable.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="seedU">A seed value to generate a deterministic generator.</param>
|
||||||
|
public UIntChannelProducer(uint seedU)
|
||||||
|
{
|
||||||
|
this.mW = seedU;
|
||||||
|
this.mZ = 362_436_069;
|
||||||
|
this.StartProducerThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a multi-threaded random number generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A multi-threaded random number generator created by this constructor is
|
||||||
|
/// deterministic. It's behaviour is not depending on the time of its creation.<br/><br/>
|
||||||
|
///
|
||||||
|
/// <b>Please note:</b> Although the number generator and all distributions are deterministic,
|
||||||
|
/// the behavior of the consuming application might be non-deterministic. This is possible if
|
||||||
|
/// the application with multiple threads consumes the numbers. The scheduling of the threads
|
||||||
|
/// is up to the operating system and might not be predictable.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="seedU">The first seed value.</param>
|
||||||
|
/// <param name="seedV">The second seed value.</param>
|
||||||
|
public UIntChannelProducer(uint seedU, uint seedV)
|
||||||
|
{
|
||||||
|
this.mW = seedU;
|
||||||
|
this.mZ = seedV;
|
||||||
|
this.StartProducerThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartProducerThread()
|
||||||
|
{
|
||||||
|
this.producerRandomUint = new Thread(() => this.RandomProducerUint(this.producerTokenSource.Token)) {IsBackground = true};
|
||||||
|
this.producerRandomUint.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
private async void RandomProducerUint(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
this.mZ = 36_969 * (this.mZ & 65_535) + (this.mZ >> 16);
|
||||||
|
this.mW = 18_000 * (this.mW & 65_535) + (this.mW >> 16);
|
||||||
|
await this.channelIntegers.Writer.WriteAsync((this.mZ << 16) + this.mW, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<uint> GetNextAsync(CancellationToken cancellationToken = default) => await this.channelIntegers.Reader.ReadAsync(cancellationToken);
|
||||||
|
|
||||||
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
|
private void StopProducer() => this.producerTokenSource.Cancel();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose() => this.StopProducer();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
327
FastRngTests/GetIntTests.cs
Normal file
327
FastRngTests/GetIntTests.cs
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using FastRng;
|
||||||
|
using FastRng.Distributions;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace FastRngTests;
|
||||||
|
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
public class GetIntTests
|
||||||
|
{
|
||||||
|
#region Channel-Based
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelGetMax()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMax = random.Next();
|
||||||
|
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMax);
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelGetMaxDistributionCheck()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMax = random.Next(3, 33);
|
||||||
|
var freqCheck = new IntFrequencyAnalysis(randomMax);
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMax);
|
||||||
|
freqCheck.CountThis(value);
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
var distribution = freqCheck.NormalizeAndPlotEvents(TestContext.WriteLine);
|
||||||
|
var max = distribution.Max();
|
||||||
|
var min = distribution.Min();
|
||||||
|
Assert.That(max - min, Is.LessThanOrEqualTo(0.27f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelGetMinMax()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMin = random.Next();
|
||||||
|
int randomMax;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
randomMax = random.Next();
|
||||||
|
} while (randomMax < randomMin);
|
||||||
|
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMin, randomMax);
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(randomMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelGetMinMaxDistributionCheck()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMin = random.Next(0, 16);
|
||||||
|
var randomMax = random.Next(randomMin + 10, 33);
|
||||||
|
|
||||||
|
var freqCheck = new IntFrequencyAnalysis(randomMax - randomMin);
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMin, randomMax);
|
||||||
|
freqCheck.CountThis(value - randomMin);
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(randomMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
var distribution = freqCheck.NormalizeAndPlotEvents(TestContext.WriteLine);
|
||||||
|
var max = distribution.Max();
|
||||||
|
var min = distribution.Min();
|
||||||
|
Assert.That(max - min, Is.LessThanOrEqualTo(0.27f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelCheckMinMax01()
|
||||||
|
{
|
||||||
|
var expectedMax = 3;
|
||||||
|
|
||||||
|
var max = int.MinValue;
|
||||||
|
var min = int.MaxValue;
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(expectedMax);
|
||||||
|
if (value < min)
|
||||||
|
min = value;
|
||||||
|
|
||||||
|
if (value > max)
|
||||||
|
max = value;
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(expectedMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(min, Is.EqualTo(0));
|
||||||
|
Assert.That(max, Is.EqualTo(expectedMax - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void ChannelCheckMinMax02()
|
||||||
|
{
|
||||||
|
var expectedMin = 2;
|
||||||
|
var expectedMax = 6;
|
||||||
|
|
||||||
|
var max = int.MinValue;
|
||||||
|
var min = int.MaxValue;
|
||||||
|
using var rng = new MultiChannelRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(expectedMin, expectedMax);
|
||||||
|
if (value < min)
|
||||||
|
min = value;
|
||||||
|
|
||||||
|
if (value > max)
|
||||||
|
max = value;
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(expectedMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(expectedMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(min, Is.EqualTo(expectedMin));
|
||||||
|
Assert.That(max, Is.EqualTo(expectedMax - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Multithreaded
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedGetMax()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMax = random.Next();
|
||||||
|
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMax);
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedGetMaxDistributionCheck()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMax = random.Next(3, 33);
|
||||||
|
var freqCheck = new IntFrequencyAnalysis(randomMax);
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMax);
|
||||||
|
freqCheck.CountThis(value);
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
var distribution = freqCheck.NormalizeAndPlotEvents(TestContext.WriteLine);
|
||||||
|
var max = distribution.Max();
|
||||||
|
var min = distribution.Min();
|
||||||
|
Assert.That(max - min, Is.LessThanOrEqualTo(0.27f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedGetMinMax()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMin = random.Next();
|
||||||
|
int randomMax;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
randomMax = random.Next();
|
||||||
|
} while (randomMax < randomMin);
|
||||||
|
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMin, randomMax);
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(randomMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedGetMinMaxDistributionCheck()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
var randomMin = random.Next(0, 16);
|
||||||
|
var randomMax = random.Next(randomMin + 10, 33);
|
||||||
|
|
||||||
|
var freqCheck = new IntFrequencyAnalysis(randomMax - randomMin);
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(randomMin, randomMax);
|
||||||
|
freqCheck.CountThis(value - randomMin);
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(randomMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(randomMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
var distribution = freqCheck.NormalizeAndPlotEvents(TestContext.WriteLine);
|
||||||
|
var max = distribution.Max();
|
||||||
|
var min = distribution.Min();
|
||||||
|
Assert.That(max - min, Is.LessThanOrEqualTo(0.27f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedCheckMinMax01()
|
||||||
|
{
|
||||||
|
var expectedMax = 3;
|
||||||
|
|
||||||
|
var max = int.MinValue;
|
||||||
|
var min = int.MaxValue;
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(expectedMax);
|
||||||
|
if (value < min)
|
||||||
|
min = value;
|
||||||
|
|
||||||
|
if (value > max)
|
||||||
|
max = value;
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(0));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(expectedMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(min, Is.EqualTo(0));
|
||||||
|
Assert.That(max, Is.EqualTo(expectedMax - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Category(TestCategories.COVER)]
|
||||||
|
[Category(TestCategories.NORMAL)]
|
||||||
|
[Category(TestCategories.INT)]
|
||||||
|
public void MultithreadedCheckMinMax02()
|
||||||
|
{
|
||||||
|
var expectedMin = 2;
|
||||||
|
var expectedMax = 6;
|
||||||
|
|
||||||
|
var max = int.MinValue;
|
||||||
|
var min = int.MaxValue;
|
||||||
|
using var rng = new MultiThreadedRng<float>();
|
||||||
|
for(var n = 0; n < 10_000; n++)
|
||||||
|
{
|
||||||
|
var value = rng.GetUniformInt(expectedMin, expectedMax);
|
||||||
|
if (value < min)
|
||||||
|
min = value;
|
||||||
|
|
||||||
|
if (value > max)
|
||||||
|
max = value;
|
||||||
|
|
||||||
|
Assert.That(value, Is.GreaterThanOrEqualTo(expectedMin));
|
||||||
|
Assert.That(value, Is.LessThanOrEqualTo(expectedMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(min, Is.EqualTo(expectedMin));
|
||||||
|
Assert.That(max, Is.EqualTo(expectedMax - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -5,6 +5,7 @@ public static class TestCategories
|
|||||||
public const string COVER = "cover";
|
public const string COVER = "cover";
|
||||||
public const string PERFORMANCE = "performance";
|
public const string PERFORMANCE = "performance";
|
||||||
public const string NORMAL = "normal";
|
public const string NORMAL = "normal";
|
||||||
|
public const string INT = "int";
|
||||||
public const string EXAMPLE = "example";
|
public const string EXAMPLE = "example";
|
||||||
public const string LONG_RUNNING = "long running";
|
public const string LONG_RUNNING = "long running";
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user