summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/Random.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/Random.cs')
-rw-r--r--src/mscorlib/shared/System/Random.cs274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Random.cs b/src/mscorlib/shared/System/Random.cs
new file mode 100644
index 0000000000..a66a9ea423
--- /dev/null
+++ b/src/mscorlib/shared/System/Random.cs
@@ -0,0 +1,274 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+** Purpose: A random number generator.
+**
+**
+===========================================================*/
+
+using System;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Globalization;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ [Serializable]
+ public class Random
+ {
+ //
+ // Private Constants
+ //
+ private const int MBIG = Int32.MaxValue;
+ private const int MSEED = 161803398;
+ private const int MZ = 0;
+
+
+ //
+ // Member Variables
+ //
+ private int _inext;
+ private int _inextp;
+ private int[] _seedArray = new int[56];
+
+ //
+ // Public Constants
+ //
+
+ //
+ // Native Declarations
+ //
+
+ //
+ // Constructors
+ //
+
+ /*=========================================================================================
+ **Action: Initializes a new instance of the Random class, using a default seed value
+ ===========================================================================================*/
+ public Random()
+ : this(GenerateSeed())
+ {
+ }
+
+ /*=========================================================================================
+ **Action: Initializes a new instance of the Random class, using a specified seed value
+ ===========================================================================================*/
+ public Random(int Seed)
+ {
+ int ii = 0;
+ int mj, mk;
+
+ //Initialize our Seed array.
+ int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
+ mj = MSEED - subtraction;
+ _seedArray[55] = mj;
+ mk = 1;
+ for (int i = 1; i < 55; i++)
+ { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
+ if ((ii += 21) >= 55) ii -= 55;
+ _seedArray[ii] = mk;
+ mk = mj - mk;
+ if (mk < 0) mk += MBIG;
+ mj = _seedArray[ii];
+ }
+ for (int k = 1; k < 5; k++)
+ {
+ for (int i = 1; i < 56; i++)
+ {
+ int n = i + 30;
+ if (n >= 55) n -= 55;
+ _seedArray[i] -= _seedArray[1 + n];
+ if (_seedArray[i] < 0) _seedArray[i] += MBIG;
+ }
+ }
+ _inext = 0;
+ _inextp = 21;
+ Seed = 1;
+ }
+
+ //
+ // Package Private Methods
+ //
+
+ /*====================================Sample====================================
+ **Action: Return a new random number [0..1) and reSeed the Seed array.
+ **Returns: A double [0..1)
+ **Arguments: None
+ **Exceptions: None
+ ==============================================================================*/
+ protected virtual double Sample()
+ {
+ //Including this division at the end gives us significantly improved
+ //random number distribution.
+ return (InternalSample() * (1.0 / MBIG));
+ }
+
+ private int InternalSample()
+ {
+ int retVal;
+ int locINext = _inext;
+ int locINextp = _inextp;
+
+ if (++locINext >= 56) locINext = 1;
+ if (++locINextp >= 56) locINextp = 1;
+
+ retVal = _seedArray[locINext] - _seedArray[locINextp];
+
+ if (retVal == MBIG) retVal--;
+ if (retVal < 0) retVal += MBIG;
+
+ _seedArray[locINext] = retVal;
+
+ _inext = locINext;
+ _inextp = locINextp;
+
+ return retVal;
+ }
+
+ [ThreadStatic]
+ private static Random t_threadRandom;
+ private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed());
+
+ /*=====================================GenerateSeed=====================================
+ **Returns: An integer that can be used as seed values for consecutively
+ creating lots of instances on the same thread within a short period of time.
+ ========================================================================================*/
+ private static int GenerateSeed()
+ {
+ Random rnd = t_threadRandom;
+ if (rnd == null)
+ {
+ int seed;
+ lock (s_globalRandom)
+ {
+ seed = s_globalRandom.Next();
+ }
+ rnd = new Random(seed);
+ t_threadRandom = rnd;
+ }
+ return rnd.Next();
+ }
+
+ /*==================================GenerateGlobalSeed====================================
+ **Action: Creates a number to use as global seed.
+ **Returns: An integer that is safe to use as seed values for thread-local seed generators.
+ ==========================================================================================*/
+ private static unsafe int GenerateGlobalSeed()
+ {
+ int result;
+ Interop.GetRandomBytes((byte*)&result, sizeof(int));
+ return result;
+ }
+
+ //
+ // Public Instance Methods
+ //
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [0..Int32.MaxValue)
+ **Arguments: None
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next()
+ {
+ return InternalSample();
+ }
+
+ private double GetSampleForLargeRange()
+ {
+ // The distribution of double value returned by Sample
+ // is not distributed well enough for a large range.
+ // If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
+ // We will end up getting even numbers only.
+
+ int result = InternalSample();
+ // Note we can't use addition here. The distribution will be bad if we do that.
+ bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample
+ if (negative)
+ {
+ result = -result;
+ }
+ double d = result;
+ d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
+ d /= 2 * (uint)Int32.MaxValue - 1;
+ return d;
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [minvalue..maxvalue)
+ **Arguments: minValue -- the least legal value for the Random number.
+ ** maxValue -- One greater than the greatest legal return value.
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next(int minValue, int maxValue)
+ {
+ if (minValue > maxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue)));
+ }
+ Contract.EndContractBlock();
+
+ long range = (long)maxValue - minValue;
+ if (range <= (long)Int32.MaxValue)
+ {
+ return ((int)(Sample() * range) + minValue);
+ }
+ else
+ {
+ return (int)((long)(GetSampleForLargeRange() * range) + minValue);
+ }
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [0..maxValue)
+ **Arguments: maxValue -- One more than the greatest legal return value.
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next(int maxValue)
+ {
+ if (maxValue < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue)));
+ }
+ Contract.EndContractBlock();
+ return (int)(Sample() * maxValue);
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: A double [0..1)
+ **Arguments: None
+ **Exceptions: None
+ ==============================================================================*/
+ public virtual double NextDouble()
+ {
+ return Sample();
+ }
+
+
+ /*==================================NextBytes===================================
+ **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
+ **Returns:Void
+ **Arguments: buffer -- the array to be filled.
+ **Exceptions: None
+ ==============================================================================*/
+ public virtual void NextBytes(byte[] buffer)
+ {
+ if (buffer == null) throw new ArgumentNullException(nameof(buffer));
+ Contract.EndContractBlock();
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
+ }
+ }
+ }
+}