-
-
Save brianrourkeboll/9935b6aa35a2227610a4251b38404729 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.Collections.Generic | |
open System.Runtime.CompilerServices | |
open System.Security.Cryptography | |
[<Sealed>] | |
type internal ThreadSafeRandom() = | |
inherit Random() | |
[<DefaultValue>] | |
[<ThreadStatic>] | |
static val mutable private random: Random | |
[<MethodImpl(MethodImplOptions.NoInlining)>] | |
static member private Create() = | |
ThreadSafeRandom.random <- Random() | |
ThreadSafeRandom.random | |
static member private LocalRandom = | |
match ThreadSafeRandom.random with | |
| null -> ThreadSafeRandom.Create() | |
| random -> random | |
override _.Next() = ThreadSafeRandom.LocalRandom.Next() | |
override _.Next maxValue = ThreadSafeRandom.LocalRandom.Next maxValue | |
override _.Next(minValue, maxValue) = ThreadSafeRandom.LocalRandom.Next(minValue, maxValue) | |
override _.NextDouble() = ThreadSafeRandom.LocalRandom.NextDouble() | |
override _.NextBytes(buffer: byte array) = ThreadSafeRandom.LocalRandom.NextBytes buffer | |
override _.Sample() = raise (NotSupportedException()) | |
[<AutoOpen>] | |
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] | |
module internal ThreadSafeRandom = | |
// Avoid the static init check overhead on each access | |
// that would happen if this were a static member val in a class. | |
// See: https://github.com/dotnet/fsharp/issues/6454 | |
let private shared = ThreadSafeRandom() | |
type Random with | |
static member internal Shared: Random = shared | |
[<RequireQualifiedAccess>] | |
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] | |
module Random = | |
let nextInt () = Random.Shared.Next() | |
let int maxValue = Random.Shared.Next maxValue | |
let intInRange minValue maxValue = Random.Shared.Next(minValue, maxValue) | |
let nextFloat () = Random.Shared.NextDouble() | |
let bytes count = | |
if count < 0 then invalidArg (nameof count) "…" | |
let bytes = Array.zeroCreate count | |
Random.Shared.NextBytes bytes | |
bytes | |
let fill (buffer: byte array) = Random.Shared.NextBytes buffer | |
module Array = | |
let inline shuffle ([<InlineIfLambda>] randomizer) (array: 'T array) = | |
if isNull array then | |
nullArg (nameof array) | |
let array = Unchecked.unbox<'T array> (array.Clone()) | |
for i in array.Length - 1 .. -1 .. 1 do | |
let j = randomizer (i + 1) | |
if j < 0 || i < j then invalidArg (nameof randomizer) "…" | |
let temp = array[i] | |
array[i] <- array[j] | |
array[j] <- temp | |
array | |
let choice randomizer (array: 'T array) = | |
if isNull array then | |
nullArg (nameof array) | |
if array.Length = 0 then | |
invalidArg (nameof array) "…" | |
let i = randomizer array.Length | |
if i < 0 then | |
invalidArg (nameof randomizer) "…" | |
if array.Length <= i then | |
invalidArg (nameof randomizer) "…" | |
array[i] | |
let inline choices ([<InlineIfLambda>] randomizer) count (array: 'T array) = | |
if isNull array then | |
nullArg (nameof array) | |
if count < 0 then | |
invalidArg (nameof count) "…" | |
if array.Length < count then | |
invalidArg (nameof count) "…" | |
if array.Length = 0 then | |
invalidArg (nameof array) "…" | |
[| | |
for _ in 1 .. count do | |
let i = randomizer array.Length | |
if i < 0 then | |
invalidArg (nameof randomizer) "…" | |
if array.Length <= i then | |
invalidArg (nameof randomizer) "…" | |
yield array[i] | |
|] | |
let inline sample ([<InlineIfLambda>] randomizer) count (array: 'T array) = | |
if isNull array then | |
nullArg (nameof array) | |
if count < 0 then | |
invalidArg (nameof count) "…" | |
if array.Length = 0 then | |
invalidArg (nameof array) "…" | |
// TODO: Switch between impls depending on count. | |
let seen = HashSet() | |
[| | |
for _ in 0 .. count do | |
let rec nextInt () = | |
let i = randomizer array.Length | |
if i < 0 then | |
invalidArg (nameof randomizer) "…" | |
if array.Length <= i then | |
invalidArg (nameof randomizer) "…" | |
if seen.Add i then i else nextInt () | |
let i = nextInt () | |
yield array[i] | |
|] | |
module WithRandomModule = | |
// Using the Random module. | |
let shuffled = [|1..100|] |> Array.shuffle Random.int | |
let choice = [|1..100|] |> Array.choice Random.int | |
let choices = [|1..100|] |> Array.choices Random.int 33 | |
let sample = [|1..100|] |> Array.sample Random.int 33 | |
module NoRandomModule = | |
// Directly calling Random.Shared.Next. | |
let shuffled = [|1..100|] |> Array.shuffle Random.Shared.Next | |
let choice = [|1..100|] |> Array.choice Random.Shared.Next | |
let choices = [|1..100|] |> Array.choices Random.Shared.Next 33 | |
let sample = [|1..100|] |> Array.sample Random.Shared.Next 33 | |
module RandomNumberGenerator = | |
// Directly calling System.Security.Cryptography.RandomNumberGenerator.GetInt32. | |
let shuffled = [|1..100|] |> Array.shuffle RandomNumberGenerator.GetInt32 | |
let choice = [|1..100|] |> Array.choice RandomNumberGenerator.GetInt32 | |
let choices = [|1..100|] |> Array.choices RandomNumberGenerator.GetInt32 33 | |
let sample = [|1..100|] |> Array.sample RandomNumberGenerator.GetInt32 33 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment