// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2016 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Runtime.Serialization; namespace IStation.Numerics.Random { /// /// Base class for random number generators. This class introduces a layer between /// and the Math.Net Numerics random number generators to provide thread safety. /// When used directly it use the System.Random as random number source. /// [Serializable] [DataContract(Namespace = "urn:IStation/Numerics/Random")] public abstract class RandomSource : System.Random { readonly bool _threadSafe; readonly object _lock = new object(); /// /// Initializes a new instance of the class using /// the value of to set whether /// the instance is thread safe or not. /// protected RandomSource() : base(RandomSeed.Robust()) { _threadSafe = Control.ThreadSafeRandomNumberGenerators; } /// /// Initializes a new instance of the class. /// /// if set to true , the class is thread safe. /// Thread safe instances are two and half times slower than non-thread /// safe classes. protected RandomSource(bool threadSafe) : base(RandomSeed.Robust()) { _threadSafe = threadSafe; } /// /// Fills an array with uniform random numbers greater than or equal to 0.0 and less than 1.0. /// /// The array to fill with random values. public void NextDoubles(double[] values) { if (_threadSafe) { lock (_lock) { for (var i = 0; i < values.Length; i++) { values[i] = DoSample(); } } } else { for (var i = 0; i < values.Length; i++) { values[i] = DoSample(); } } } /// /// Returns an array of uniform random numbers greater than or equal to 0.0 and less than 1.0. /// /// The size of the array to fill. public double[] NextDoubles(int count) { var values = new double[count]; NextDoubles(values); return values; } /// /// Returns an infinite sequence of uniform random numbers greater than or equal to 0.0 and less than 1.0. /// public IEnumerable NextDoubleSequence() { for (int i = 0; i < 64; i++) { yield return NextDouble(); } var buffer = new double[64]; while (true) { NextDoubles(buffer); for (int i = 0; i < buffer.Length; i++) { yield return buffer[i]; } } } /// /// Returns a random 32-bit signed integer greater than or equal to zero and less than . /// public sealed override int Next() { if (_threadSafe) { lock (_lock) { return DoSampleInteger(); } } else { return DoSampleInteger(); } } /// /// Returns a random number less then a specified maximum. /// /// The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1. /// A 32-bit signed integer less than . /// is zero or negative. public sealed override int Next(int maxExclusive) { // Invalid case: Zero and less are not valid use cases. if (maxExclusive <= 0) { throw new ArgumentException("Value must be positive."); } // Fast case: Only zero is allowed to be returned. No sampling is needed. if (maxExclusive == 1) { return 0; } // Simple case: standard range if (maxExclusive == int.MaxValue) { return Next(); } // Sample with maxExclusive ≥ 2 if (_threadSafe) { lock (_lock) { return DoSampleInteger(maxExclusive); } } else { return DoSampleInteger(maxExclusive); } } /// /// Returns a random number within a specified range. /// /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive. /// /// A 32-bit signed integer greater than or equal to and less than ; that is, the range of return values includes but not . If equals , is returned. /// /// is greater than . public sealed override int Next(int minInclusive, int maxExclusive) { // Invalid case: empty range. if (minInclusive >= maxExclusive) { throw new ArgumentException("In the specified range, the exclusive maximum must be greater than the inclusive minimum."); } // Fast case: Only minInclusive is allowed to be returned. No sampling is needed. if (maxExclusive == minInclusive + 1) { return minInclusive; } // Simple case: simple range if (minInclusive == 0) { // Simple case: standard range if (maxExclusive == int.MaxValue) { return Next(); } return Next(maxExclusive); } // Sample with maxExclusive ≥ minExclusive + 2 if (_threadSafe) { lock (_lock) { return DoSampleInteger(minInclusive, maxExclusive); } } else { return DoSampleInteger(minInclusive, maxExclusive); } } /// /// Fills an array with random 32-bit signed integers greater than or equal to zero and less than . /// /// The array to fill with random values. public void NextInt32s(int[] values) { if (_threadSafe) { lock (_lock) { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(); } } } else { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(); } } } /// /// Returns an array with random 32-bit signed integers greater than or equal to zero and less than . /// /// The size of the array to fill. public int[] NextInt32s(int count) { var values = new int[count]; NextInt32s(values); return values; } /// /// Fills an array with random numbers within a specified range. /// /// The array to fill with random values. /// The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1. public void NextInt32s(int[] values, int maxExclusive) { // Invalid case: Zero and less are not valid use cases. if (maxExclusive <= 0) { throw new ArgumentException("Value must be positive."); } // Fast case: Only zero is allowed to be returned. No sampling is needed. if (maxExclusive == 1) { Array.Clear(values, 0, values.Length); return; } // Simple case: standard range if (maxExclusive == int.MaxValue) { NextInt32s(values); return; } // Sample with maxExclusive ≥ 2 if (_threadSafe) { lock (_lock) { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(maxExclusive); } } } else { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(maxExclusive); } } } /// /// Returns an array with random 32-bit signed integers within the specified range. /// /// The size of the array to fill. /// The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 1. public int[] NextInt32s(int count, int maxExclusive) { var values = new int[count]; NextInt32s(values, maxExclusive); return values; } /// /// Fills an array with random numbers within a specified range. /// /// The array to fill with random values. /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive. public void NextInt32s(int[] values, int minInclusive, int maxExclusive) { // Invalid case: empty range. if (minInclusive >= maxExclusive) { throw new ArgumentException("In the specified range, the exclusive maximum must be greater than the inclusive minimum."); } // Fast case: Only minInclusive is allowed to be returned. No sampling is needed. if (maxExclusive == minInclusive + 1) { for (var i = 0; i < values.Length; i++) { values[i] = minInclusive; } return; } // Simple case: simple range if (minInclusive == 0) { // Simple case: standard range if (maxExclusive == int.MaxValue) { NextInt32s(values); return; } NextInt32s(values, maxExclusive); return; } // Sample with maxExclusive ≥ minExclusive + 2 if (_threadSafe) { lock (_lock) { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(minInclusive, maxExclusive); } } } else { for (var i = 0; i < values.Length; i++) { values[i] = DoSampleInteger(minInclusive, maxExclusive); } } } /// /// Returns an array with random 32-bit signed integers within the specified range. /// /// The size of the array to fill. /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive. public int[] NextInt32s(int count, int minInclusive, int maxExclusive) { var values = new int[count]; NextInt32s(values, minInclusive, maxExclusive); return values; } /// /// Returns an infinite sequence of random 32-bit signed integers greater than or equal to zero and less than . /// public IEnumerable NextInt32Sequence() { for (int i = 0; i < 64; i++) { yield return Next(); } var buffer = new int[64]; while (true) { NextInt32s(buffer); for (int i = 0; i < buffer.Length; i++) { yield return buffer[i]; } } } /// /// Returns an infinite sequence of random numbers within a specified range. /// /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. Range: maxExclusive > minExclusive. public IEnumerable NextInt32Sequence(int minInclusive, int maxExclusive) { if (minInclusive > maxExclusive) { throw new ArgumentException("In the specified range, the minimum is greater than maximum."); } for (int i = 0; i < 64; i++) { yield return Next(minInclusive, maxExclusive); } var buffer = new int[64]; while (true) { NextInt32s(buffer, minInclusive, maxExclusive); for (int i = 0; i < buffer.Length; i++) { yield return buffer[i]; } } } /// /// Fills the elements of a specified array of bytes with random numbers. /// /// An array of bytes to contain random numbers. /// is null. public sealed override void NextBytes(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (_threadSafe) { lock (_lock) { DoSampleBytes(buffer); } return; } DoSampleBytes(buffer); } /// /// Returns a random number between 0.0 and 1.0. /// /// A double-precision floating point number greater than or equal to 0.0, and less than 1.0. protected sealed override double Sample() { if (_threadSafe) { lock (_lock) { return DoSample(); } } return DoSample(); } /// /// Returns a random double-precision floating point number greater than or equal to 0.0, and less than 1.0. /// protected abstract double DoSample(); /// /// Returns a random 32-bit signed integer greater than or equal to zero and less than 2147483647 (). /// protected virtual int DoSampleInteger() { return (int)(DoSample() * int.MaxValue); } /// /// Fills the elements of a specified array of bytes with random numbers in full range, including zero and 255 (). /// protected virtual void DoSampleBytes(byte[] buffer) { for (var i = 0; i < buffer.Length; i++) { buffer[i] = (byte)(DoSampleInteger() % 256); } } /// /// Returns a random N-bit signed integer greater than or equal to zero and less than 2^N. /// N (bit count) is expected to be greater than zero and less than 32 (not verified). /// protected virtual int DoSampleInt32WithNBits(int bitCount) { // Fast case: Only 0 is allowed to be returned // No random call is needed if (bitCount == 0) { return 0; } var bytes = new byte[4]; DoSampleBytes(bytes); // every bit with independent uniform distribution uint uint32 = BitConverter.ToUInt32(bytes, 0); // the least significant N bits with independent uniform distribution and the remaining bits zero uint uintN = uint32 >> (32 - bitCount); return (int)uintN; } /// /// Returns a random N-bit signed long integer greater than or equal to zero and less than 2^N. /// N (bit count) is expected to be greater than zero and less than 64 (not verified). /// protected virtual long DoSampleInt64WithNBits(int bitCount) { // Fast case: Only 0 is allowed to be returned // No random call is needed if (bitCount == 0) { return 0; } var bytes = new byte[8]; DoSampleBytes(bytes); // every bit with independent uniform distribution ulong uint64 = BitConverter.ToUInt64(bytes, 0); // the least significant N bits with independent uniform distribution and the remaining bits zero ulong uintN = uint64 >> (64 - bitCount); return (long)uintN; } /// /// Returns a random 32-bit signed integer within the specified range. /// /// The exclusive upper bound of the random number returned. Range: maxExclusive ≥ 2 (not verified, must be ensured by caller). protected virtual int DoSampleInteger(int maxExclusive) { // non-biased implementation // (biased: return (int)(DoSample() * maxExclusive);) int bitCount = Euclid.Log2(maxExclusive); int range = Euclid.PowerOfTwo(bitCount); // Fast case: maxExclusive is a power of two if (range == maxExclusive) { return DoSampleInt32WithNBits(bitCount); } // Rejection case: we need to use rejection to avoid introducing bias bitCount++; int sample; do { sample = DoSampleInt32WithNBits(bitCount); } while (sample >= maxExclusive); return sample; } /// /// Returns a random 32-bit signed integer within the specified range. /// /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. Range: maxExclusive ≥ minExclusive + 2 (not verified, must be ensured by caller). protected virtual int DoSampleInteger(int minInclusive, int maxExclusive) { // Sample with maxExclusive ≥ 2 return DoSampleInteger(maxExclusive - minInclusive) + minInclusive; } } }