From eb0cbb1c6bfbd68c82ab31a89581560a9d61c978 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 29 May 2024 13:08:30 -0700 Subject: [PATCH 01/25] Random functions: old version rebased --- src/FSharp.Core/FSharp.Core.fsproj | 6 + src/FSharp.Core/Random.fs | 19 ++ src/FSharp.Core/Random.fsi | 7 + src/FSharp.Core/array.fs | 92 ++++++++++ src/FSharp.Core/array.fsi | 24 +++ src/FSharp.Core/list.fs | 83 +++++++++ src/FSharp.Core/list.fsi | 24 +++ src/FSharp.Core/local.fs | 10 ++ src/FSharp.Core/local.fsi | 3 + src/FSharp.Core/seq.fs | 82 +++++++++ src/FSharp.Core/seq.fsi | 24 +++ .../ArrayModule.fs | 165 ++++++++++++++++++ .../ListModule.fs | 152 +++++++++++++++- .../Microsoft.FSharp.Collections/SeqModule.fs | 150 ++++++++++++++++ 14 files changed, 840 insertions(+), 1 deletion(-) create mode 100644 src/FSharp.Core/Random.fs create mode 100644 src/FSharp.Core/Random.fsi diff --git a/src/FSharp.Core/FSharp.Core.fsproj b/src/FSharp.Core/FSharp.Core.fsproj index dbfbf0006e6..32aeb2fc498 100644 --- a/src/FSharp.Core/FSharp.Core.fsproj +++ b/src/FSharp.Core/FSharp.Core.fsproj @@ -91,6 +91,12 @@ Primitives/prim-types.fs + + Random/Random.fsi + + + Random/Random.fs + Collections/local.fsi diff --git a/src/FSharp.Core/Random.fs b/src/FSharp.Core/Random.fs new file mode 100644 index 00000000000..9ca3e43a477 --- /dev/null +++ b/src/FSharp.Core/Random.fs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.FSharp.Core + +open System +open System.Threading + +[] +type internal ThreadSafeRandom() = + [] static val mutable private globalRandom : Random + [] static val mutable private localRandom : ThreadLocal + static do ThreadSafeRandom.globalRandom <- Random() + static do ThreadSafeRandom.localRandom <- new ThreadLocal(fun () -> + lock ThreadSafeRandom.globalRandom (fun () -> + Random(ThreadSafeRandom.globalRandom.Next()) + ) + ) + // Don't pass the returned Random object between threads + static member Shared = ThreadSafeRandom.localRandom.Value \ No newline at end of file diff --git a/src/FSharp.Core/Random.fsi b/src/FSharp.Core/Random.fsi new file mode 100644 index 00000000000..ecb087320df --- /dev/null +++ b/src/FSharp.Core/Random.fsi @@ -0,0 +1,7 @@ +namespace Microsoft.FSharp.Core + +open System + +[] +type internal ThreadSafeRandom = + static member Shared: Random \ No newline at end of file diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 344d7e3e7cf..429e8b544db 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1936,6 +1936,98 @@ module Array = result + [] + let shuffleRand (random: Random) (source: 'T[]) : 'T[] = + checkNonNull "source" source + + let result = copy source + + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random result + + result + + [] + let shuffle (source: 'T[]) : 'T[] = + shuffleRand ThreadSafeRandom.Shared source + + [] + let choiceRand (random: Random) (source: 'T[]) : 'T = + checkNonNull "source" source + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + let i = random.Next(0, inputLength) + source.[i] + + [] + let choice (source: 'T[]) : 'T = + choiceRand ThreadSafeRandom.Shared source + + [] + let choicesRand (random: Random) (count: int) (source: 'T[]) : 'T[] = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count + + for i = 0 to count - 1 do + let j = random.Next(0, inputLength) + result.[i] <- source.[j] + result + + [] + let choices (count: int) (source: 'T[]) : 'T[] = + choicesRand ThreadSafeRandom.Shared count source + + [] + let sampleRand (random: Random) (count: int) (source: 'T[]) : 'T[] = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let mutable setSize = 21 + if count > 5 then + setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + if inputLength <= setSize then + let pool = copy source + for i = 0 to count - 1 do + let j = random.Next(0, inputLength - i) + result[i] <- pool[j] + pool[j] <- pool[inputLength - i - 1] + else + let selected = HashSet() + for i = 0 to count - 1 do + let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do + j <- random.Next(0, inputLength) + selected.Add(j) |> ignore + result[i] <- source[j] + + result + + [] + let sample (count: int) (source: 'T[]) : 'T[] = + sampleRand ThreadSafeRandom.Shared count source + module Parallel = open System.Threading open System.Threading.Tasks diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 8a5d51f0c9e..de55e526c2b 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3095,6 +3095,30 @@ module Array = [] val insertManyAt: index: int -> values: seq<'T> -> source: 'T array -> 'T array + [] + val shuffle : source: 'T[] -> 'T[] + + [] + val shuffleRand : random: Random -> source: 'T[] -> 'T[] + + [] + val choice : source: 'T[] -> 'T + + [] + val choiceRand : random: Random -> source: 'T[] -> 'T + + [] + val choices : count: int -> source: 'T[] -> 'T[] + + [] + val choicesRand : random: Random -> count: int -> source: 'T[] -> 'T[] + + [] + val sample : count: int -> source: 'T[] -> 'T[] + + [] + val sampleRand : random: Random -> count: int -> source: 'T[] -> 'T[] + /// Provides parallel operations on arrays module Parallel = diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index dc81209a8c7..a8655642061 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -2,6 +2,7 @@ namespace Microsoft.FSharp.Collections +open System open Microsoft.FSharp.Core open Microsoft.FSharp.Core.Operators open Microsoft.FSharp.Core.LanguagePrimitives @@ -987,3 +988,85 @@ module List = coll.AddMany(values) // insert values BEFORE the item at the index coll.AddManyAndClose(curr) + + [] + let shuffleRand (random: Random) (source: 'T list) : 'T list = + let tempArray = toArray source + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray + ofArray tempArray + + [] + let shuffle (source: 'T list) : 'T list = + shuffleRand ThreadSafeRandom.Shared source + + [] + let choiceRand (random: Random) (source: 'T list) : 'T = + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + let i = random.Next(0, inputLength) + source.[i] + + [] + let choice (source: 'T list) : 'T = + choiceRand ThreadSafeRandom.Shared source + + [] + let choicesRand (random: Random) (count: int) (source: 'T list) : 'T list = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + [ + for _ = 0 to count - 1 do + let j = random.Next(0, inputLength) + source.[j] + ] + + [] + let choices (count: int) (source: 'T list) : 'T list = + choicesRand ThreadSafeRandom.Shared count source + + [] + let sampleRand (random: Random) (count: int) (source: 'T list) : 'T list = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let mutable setSize = 21 + if count > 5 then + setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + if inputLength <= setSize then + let pool = source |> toArray + [ + for i = 0 to count - 1 do + let j = random.Next(0, inputLength - i) + let nextItem = pool[j] + pool[j] <- pool[inputLength - i - 1] + nextItem + ] + else + let selected = HashSet() + [ + for _ = 0 to count - 1 do + let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do + j <- random.Next(0, inputLength) + selected.Add(j) |> ignore + source[j] + ] + + [] + let sample (count: int) (source: 'T list) : 'T list = + sampleRand ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index 05ff605b247..3a47242e2d4 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2694,3 +2694,27 @@ module List = /// [] val insertManyAt: index: int -> values: seq<'T> -> source: 'T list -> 'T list + + [] + val shuffle : source: 'T list -> 'T list + + [] + val shuffleRand : random: Random -> source: 'T list -> 'T list + + [] + val choice : source: 'T list -> 'T + + [] + val choiceRand : random: Random -> source: 'T list -> 'T + + [] + val choices : count: int -> source: 'T list -> 'T list + + [] + val choicesRand : random: Random -> count: int -> source: 'T list -> 'T list + + [] + val sample : count: int -> source: 'T list -> 'T list + + [] + val sampleRand : random: Random -> count: int -> source: 'T list -> 'T list \ No newline at end of file diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index a57621c651f..ae1ecb8db32 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1174,6 +1174,16 @@ module internal Array = startIndex <- startIndex + minChunkSize res + let shuffleInPlaceRand (random:Random) (array : array<'T>) = + let inputLength = array.Length + for i = 0 to inputLength - 2 do + let j = random.Next(i, inputLength) + + if j <> i then + let temp = array[i] + array[i] <- array[j] + array[j] <- temp + module internal Seq = let tryLastV (source : seq<_>) = //checkNonNull "source" source //done in main Seq.tryLast diff --git a/src/FSharp.Core/local.fsi b/src/FSharp.Core/local.fsi index 1b210474062..d025c803433 100644 --- a/src/FSharp.Core/local.fsi +++ b/src/FSharp.Core/local.fsi @@ -26,6 +26,7 @@ module internal DetailedExceptions = // Definitions internal for this library. namespace Microsoft.FSharp.Primitives.Basics +open System open Microsoft.FSharp.Core open Microsoft.FSharp.Collections @@ -119,5 +120,7 @@ module internal Array = val stableSortInPlace: array: 'T array -> unit when 'T: comparison + val shuffleInPlaceRand: random: Random -> array: 'T[] -> unit + module internal Seq = val tryLastV: 'T seq -> 'T ValueOption diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index bfe050f9f7c..173be5ca183 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1938,3 +1938,85 @@ module Seq = if i < index then invalidArg "index" "index must be within bounds of the array" } + + [] + let shuffleRand (random: Random) (source: seq<'T>) : seq<'T> = + let tempArray = toArray source + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray + ofArray tempArray + + [] + let shuffle (source: seq<'T>) : seq<'T> = + shuffleRand ThreadSafeRandom.Shared source + + [] + let choiceRand (random: Random) (source: seq<'T>) : 'T = + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + let i = random.Next(0, inputLength) + source |> item i + + [] + let choice (source: seq<'T>) : 'T = + choiceRand ThreadSafeRandom.Shared source + + [] + let choicesRand (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + seq { + for _ = 0 to count - 1 do + let j = random.Next(0, inputLength) + source |> item j + } + + [] + let choices (count: int) (source: seq<'T>) : seq<'T> = + choicesRand ThreadSafeRandom.Shared count source + + [] + let sampleRand (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let mutable setSize = 21 + if count > 5 then + setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + if inputLength <= setSize then + let pool = source |> toArray + seq { + for i = 0 to count - 1 do + let j = random.Next(0, inputLength - i) + let nextItem = pool[j] + pool[j] <- pool[inputLength - i - 1] + nextItem + } + else + let selected = HashSet() + seq { + for _ = 0 to count - 1 do + let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do + j <- random.Next(0, inputLength) + selected.Add(j) |> ignore + source |> item j + } + + [] + let sample (count: int) (source: seq<'T>) : seq<'T> = + sampleRand ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 4bdf2a54d6d..d14826a1729 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2935,3 +2935,27 @@ module Seq = /// [] val insertManyAt: index: int -> values: seq<'T> -> source: seq<'T> -> seq<'T> + + [] + val shuffle : source: seq<'T> -> seq<'T> + + [] + val shuffleRand : random: Random -> source: seq<'T> -> seq<'T> + + [] + val choice : source: seq<'T> -> 'T + + [] + val choiceRand : random: Random -> source: seq<'T> -> 'T + + [] + val choices : count: int -> source: seq<'T> -> seq<'T> + + [] + val choicesRand : random: Random -> count: int -> source: seq<'T> -> seq<'T> + + [] + val sample : count: int -> source: seq<'T> -> seq<'T> + + [] + val sampleRand : random: Random -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 0f9931e0e6a..93535448985 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -1962,3 +1962,168 @@ type ArrayModule() = Assert.AreEqual(arr.[-4..(-3)], ([||]: int array)) + [] + member _.Shuffle() = + let arr = [|1;2;3;4;5;6;7;8|] + + // try shuffle three times, if it doesn't shuffle, it must be broken + let mutable isShuffled = false + let mutable i = 0 + while not isShuffled && i < 3 do + let shuffled = arr |> Array.shuffle + isShuffled <- not (arr = shuffled) + i <- i + 1 + Assert.NotEqual(3, i) + + // null array + let nullArr = null + CheckThrowsArgumentNullException (fun () -> Array.shuffle nullArr |> ignore) + + [] + member _.ShuffleRand() = + let arr = [|1;2;3;4;5;6;7;8|] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = arr |> Array.shuffleRand rand1 + let shuffle2 = arr |> Array.shuffleRand rand2 + let shuffle3 = arr |> Array.shuffleRand rand3 + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(arr, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.Choice() = + let arr = [|1;2;3;4;5;6;7;8|] + + // try choice six times, if all are same, it must be broken + let results = [| + Array.choice arr + Array.choice arr + Array.choice arr + Array.choice arr + Array.choice arr + Array.choice arr + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + Assert.False(allSame) + + // null array + let nullArr = null + CheckThrowsArgumentNullException (fun () -> Array.choice nullArr |> ignore) + + // empty array + let emptyArr = [||] + CheckThrowsArgumentException (fun () -> Array.choice emptyArr |> ignore) + + [] + member _.ChoiceRand() = + let arr = [|1;2;3;4;5;6;7;8|] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = arr |> Array.choiceRand rand1 + let choice2 = arr |> Array.choiceRand rand2 + let choice3 = arr |> Array.choiceRand rand3 + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Choices() = + let arr = [|1;2;3;4;5;6;7;8|] + + // try choices three times, if all are same, it must be broken + let choicesLength = 5 + let results = [| + Array.choices choicesLength arr + Array.choices choicesLength arr + Array.choices choicesLength arr + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) + Assert.False(allSame) + Assert.True(allCorrectLength) + + // null array + let nullArr = null + CheckThrowsArgumentNullException (fun () -> Array.choices choicesLength nullArr |> ignore) + + // empty array + let emptyArr = [||] + CheckThrowsArgumentException (fun () -> Array.choices choicesLength emptyArr |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> Array.choices negativeChoicesLength arr |> ignore) + + [] + member _.ChoicesRand() = + let arr = [|1;2;3;4;5;6;7;8|] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = arr |> Array.choicesRand rand1 choicesLength + let choice2 = arr |> Array.choicesRand rand2 choicesLength + let choice3 = arr |> Array.choicesRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Sample() = + let arr = Array.init 50 id + let choicesLengthSmall = 1 + let choicesLengthLarge = 49 + + let verify choicesLength tryCount = + // try sample tryCount times, if all are same, it must be broken + let results = [| + for _ in 1..tryCount do + Array.sample choicesLength arr + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) + let allDistinct = results |> Array.forall (fun x -> x = (x |> Array.distinct)) + Assert.False(allSame) + Assert.True(allCorrectLength) + Assert.True(allDistinct) + + verify choicesLengthSmall 10 + verify choicesLengthLarge 2 + + // null array + let nullArr = null + CheckThrowsArgumentNullException (fun () -> Array.sample choicesLengthSmall nullArr |> ignore) + + // empty array + let emptyArr = [||] + CheckThrowsArgumentException (fun () -> Array.sample choicesLengthSmall emptyArr |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> Array.sample negativeChoicesLength arr |> ignore) + + // invalid count + let invalidCountRange = 100 + CheckThrowsArgumentException (fun () -> Array.sample invalidCountRange arr |> ignore) + + [] + member _.SampleRand() = + let arr = [|1;2;3;4;5;6;7;8|] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = arr |> Array.sampleRand rand1 choicesLength + let choice2 = arr |> Array.sampleRand rand2 choicesLength + let choice3 = arr |> Array.sampleRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs index fa245c76c87..46f8209b0dd 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs @@ -1086,4 +1086,154 @@ type ListModule() = member _.``Get item with reverse index behaves as expected``() = let list = [1;2;3;4;5] - Assert.AreEqual(list.[^1], 4) \ No newline at end of file + Assert.AreEqual(list.[^1], 4) + + [] + member _.Shuffle() = + let list = [1;2;3;4;5;6;7;8] + + // try shuffle three times, if it doesn't shuffle, it must be broken + let mutable isShuffled = false + let mutable i = 0 + while not isShuffled && i < 3 do + let shuffled = list |> List.shuffle + isShuffled <- not (list = shuffled) + i <- i + 1 + Assert.NotEqual(3, i) + + [] + member _.ShuffleRand() = + let list = [1;2;3;4;5;6;7;8] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = list |> List.shuffleRand rand1 + let shuffle2 = list |> List.shuffleRand rand2 + let shuffle3 = list |> List.shuffleRand rand3 + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(list, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.Choice() = + let list = [1;2;3;4;5;6;7;8] + + // try choice six times, if all are same, it must be broken + let results = [| + List.choice list + List.choice list + List.choice list + List.choice list + List.choice list + List.choice list + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + Assert.False(allSame) + + // empty list + let emptyList = [] + CheckThrowsArgumentException (fun () -> List.choice emptyList |> ignore) + + [] + member _.ChoiceRand() = + let list = [1;2;3;4;5;6;7;8] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = list |> List.choiceRand rand1 + let choice2 = list |> List.choiceRand rand2 + let choice3 = list |> List.choiceRand rand3 + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Choices() = + let list = [1;2;3;4;5;6;7;8] + + // try choices three times, if all are same, it must be broken + let choicesLength = 5 + let results = [| + List.choices choicesLength list + List.choices choicesLength list + List.choices choicesLength list + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) + Assert.False(allSame) + Assert.True(allCorrectLength) + + // empty list + let emptyList = [] + CheckThrowsArgumentException (fun () -> List.choices choicesLength emptyList |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> List.choices negativeChoicesLength list |> ignore) + + [] + member _.ChoicesRand() = + let list = [1;2;3;4;5;6;7;8] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = list |> List.choicesRand rand1 choicesLength + let choice2 = list |> List.choicesRand rand2 choicesLength + let choice3 = list |> List.choicesRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Sample() = + let list = List.init 50 id + let choicesLengthSmall = 1 + let choicesLengthLarge = 49 + + let verify choicesLength tryCount = + // try sample tryCount times, if all are same, it must be broken + let results = [| + for _ in 1..tryCount do + List.sample choicesLength list + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) + let allDistinct = results |> Array.forall (fun x -> x = (x |> List.distinct)) + Assert.False(allSame) + Assert.True(allCorrectLength) + Assert.True(allDistinct) + + verify choicesLengthSmall 10 + verify choicesLengthLarge 2 + + // empty list + let emptyList = [] + CheckThrowsArgumentException (fun () -> List.sample choicesLengthSmall emptyList |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> List.sample negativeChoicesLength list |> ignore) + + // invalid count + let invalidCountRange = 100 + CheckThrowsArgumentException (fun () -> List.sample invalidCountRange list |> ignore) + + [] + member _.SampleRand() = + let list = [1;2;3;4;5;6;7;8] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = list |> List.sampleRand rand1 choicesLength + let choice2 = list |> List.sampleRand rand2 choicesLength + let choice3 = list |> List.sampleRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index 749203601fe..5ca30b0d247 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1160,3 +1160,153 @@ type SeqModule() = let nullSeq:seq<'a> = null CheckThrowsArgumentNullException (fun () -> Seq.contains 5 nullSeq |> ignore) + + [] + member _.Shuffle() = + let seq = seq {1;2;3;4;5;6;7;8} + + // try shuffle three times, if it doesn't shuffle, it must be broken + let mutable isShuffled = false + let mutable i = 0 + while not isShuffled && i < 3 do + let shuffled = seq |> Seq.shuffle + isShuffled <- not (seq = shuffled) + i <- i + 1 + Assert.NotEqual(3, i) + + [] + member _.ShuffleRand() = + let seq = seq {1;2;3;4;5;6;7;8} + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = seq |> Seq.shuffleRand rand1 |> Seq.cache + let shuffle2 = seq |> Seq.shuffleRand rand2 |> Seq.cache + let shuffle3 = seq |> Seq.shuffleRand rand3 |> Seq.cache + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(seq, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.Choice() = + let seq = seq {1;2;3;4;5;6;7;8} + + // try choice six times, if all are same, it must be broken + let results = [| + Seq.choice seq + Seq.choice seq + Seq.choice seq + Seq.choice seq + Seq.choice seq + Seq.choice seq + |] + let allSame = results |> Array.forall (fun x -> x = results.[0]) + Assert.False(allSame) + + // empty seq + let emptySeq = Seq.empty + CheckThrowsArgumentException (fun () -> Seq.choice emptySeq |> ignore) + + [] + member _.ChoiceRand() = + let seq = seq {1;2;3;4;5;6;7;8} + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = seq |> Seq.choiceRand rand1 + let choice2 = seq |> Seq.choiceRand rand2 + let choice3 = seq |> Seq.choiceRand rand3 + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Choices() = + let seq = seq {1;2;3;4;5;6;7;8} + + // try choices three times, if all are same, it must be broken + let choicesLength = 5 + let results = [| + Seq.choices choicesLength seq |> Seq.cache + Seq.choices choicesLength seq |> Seq.cache + Seq.choices choicesLength seq |> Seq.cache + |] + let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) + let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) + Assert.False(allSame) + Assert.True(allCorrectLength) + + // empty seq + let emptySeq = Seq.empty + CheckThrowsArgumentException (fun () -> Seq.choices choicesLength emptySeq |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> Seq.choices negativeChoicesLength seq |> ignore) + + [] + member _.ChoicesRand() = + let seq = seq {1;2;3;4;5;6;7;8} + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = seq |> Seq.choicesRand rand1 choicesLength + let choice2 = seq |> Seq.choicesRand rand2 choicesLength + let choice3 = seq |> Seq.choicesRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.Sample() = + let seq = Seq.init 50 id + let choicesLengthSmall = 1 + let choicesLengthLarge = 49 + + let verify choicesLength tryCount = + // try sample tryCount times, if all are same, it must be broken + let results = [| + for _ in 1..tryCount do + Seq.sample choicesLength seq |> Seq.cache + |] + let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) + let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) + let allDistinct = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x (x |> Seq.distinct)) = 0) + Assert.False(allSame) + Assert.True(allCorrectLength) + Assert.True(allDistinct) + + verify choicesLengthSmall 10 + verify choicesLengthLarge 2 + + // empty seq + let emptySeq = Seq.empty + CheckThrowsArgumentException (fun () -> Seq.sample choicesLengthSmall emptySeq |> ignore) + + // negative choices length + let negativeChoicesLength = -1 + CheckThrowsArgumentException (fun () -> Seq.sample negativeChoicesLength seq |> ignore) + + // invalid count + let invalidCountRange = 100 + CheckThrowsArgumentException (fun () -> Seq.sample invalidCountRange seq |> ignore) + + [] + member _.SampleRand() = + let seq = seq {1;2;3;4;5;6;7;8} + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 5 + let choice1 = seq |> Seq.sampleRand rand1 choicesLength + let choice2 = seq |> Seq.sampleRand rand2 choicesLength + let choice3 = seq |> Seq.sampleRand rand3 choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) \ No newline at end of file From a7986bd84e313056a1e4e847ca59844683c0d21a Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 29 May 2024 17:12:16 -0700 Subject: [PATCH 02/25] Random functions: Rename functions according to the RFC --- src/FSharp.Core/array.fs | 40 +++++------ src/FSharp.Core/array.fsi | 66 ++++++++++++++----- src/FSharp.Core/list.fs | 40 +++++------ src/FSharp.Core/list.fsi | 63 +++++++++++++----- src/FSharp.Core/seq.fs | 40 +++++------ src/FSharp.Core/seq.fsi | 66 ++++++++++++++----- .../ArrayModule.fs | 66 +++++++++---------- .../ListModule.fs | 58 ++++++++-------- .../Microsoft.FSharp.Collections/SeqModule.fs | 58 ++++++++-------- 9 files changed, 298 insertions(+), 199 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 429e8b544db..2fc2399a42b 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1936,8 +1936,8 @@ module Array = result - [] - let shuffleRand (random: Random) (source: 'T[]) : 'T[] = + [] + let randomShuffleWith (random: Random) (source: 'T[]) : 'T[] = checkNonNull "source" source let result = copy source @@ -1946,12 +1946,12 @@ module Array = result - [] - let shuffle (source: 'T[]) : 'T[] = - shuffleRand ThreadSafeRandom.Shared source + [] + let randomShuffle (source: 'T[]) : 'T[] = + randomShuffleWith ThreadSafeRandom.Shared source - [] - let choiceRand (random: Random) (source: 'T[]) : 'T = + [] + let randomChoiceWith (random: Random) (source: 'T[]) : 'T = checkNonNull "source" source let inputLength = source.Length @@ -1961,12 +1961,12 @@ module Array = let i = random.Next(0, inputLength) source.[i] - [] - let choice (source: 'T[]) : 'T = - choiceRand ThreadSafeRandom.Shared source + [] + let randomChoice (source: 'T[]) : 'T = + randomChoiceWith ThreadSafeRandom.Shared source - [] - let choicesRand (random: Random) (count: int) (source: 'T[]) : 'T[] = + [] + let randomChoicesWith (random: Random) (count: int) (source: 'T[]) : 'T[] = checkNonNull "source" source if count < 0 then @@ -1983,12 +1983,12 @@ module Array = result.[i] <- source.[j] result - [] - let choices (count: int) (source: 'T[]) : 'T[] = - choicesRand ThreadSafeRandom.Shared count source + [] + let randomChoices (count: int) (source: 'T[]) : 'T[] = + randomChoicesWith ThreadSafeRandom.Shared count source - [] - let sampleRand (random: Random) (count: int) (source: 'T[]) : 'T[] = + [] + let randomSampleWith (random: Random) (count: int) (source: 'T[]) : 'T[] = checkNonNull "source" source if count < 0 then @@ -2024,9 +2024,9 @@ module Array = result - [] - let sample (count: int) (source: 'T[]) : 'T[] = - sampleRand ThreadSafeRandom.Shared count source + [] + let randomSample (count: int) (source: 'T[]) : 'T[] = + randomSampleWith ThreadSafeRandom.Shared count source module Parallel = open System.Threading diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index de55e526c2b..8bd25d15aa3 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3095,29 +3095,63 @@ module Array = [] val insertManyAt: index: int -> values: seq<'T> -> source: 'T array -> 'T array - [] - val shuffle : source: 'T[] -> 'T[] + /// Return a new array shuffled in a random order. + /// + /// The input array. + /// + /// The result array. + /// + /// Thrown when the input array is null. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffle + /// + /// Can evaluate to [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffle : source: 'T[] -> 'T[] - [] - val shuffleRand : random: Random -> source: 'T[] -> 'T[] + /// Return a new array shuffled in a random order with the specified Random instance. + /// + /// The Random instance. + /// The input array. + /// + /// The result array. + /// + /// Thrown when the input array is null. + /// Thrown when the random argument is null. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffleWith Random.Shared + /// + /// Can evaluate to [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffleWith : random: Random -> source: 'T[] -> 'T[] - [] - val choice : source: 'T[] -> 'T + [] + val randomChoice : source: 'T[] -> 'T - [] - val choiceRand : random: Random -> source: 'T[] -> 'T + [] + val randomChoiceWith : random: Random -> source: 'T[] -> 'T - [] - val choices : count: int -> source: 'T[] -> 'T[] + [] + val randomChoices : count: int -> source: 'T[] -> 'T[] - [] - val choicesRand : random: Random -> count: int -> source: 'T[] -> 'T[] + [] + val randomChoicesWith : random: Random -> count: int -> source: 'T[] -> 'T[] - [] - val sample : count: int -> source: 'T[] -> 'T[] + [] + val randomSample : count: int -> source: 'T[] -> 'T[] - [] - val sampleRand : random: Random -> count: int -> source: 'T[] -> 'T[] + [] + val randomSampleWith : random: Random -> count: int -> source: 'T[] -> 'T[] /// Provides parallel operations on arrays module Parallel = diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index a8655642061..3e7bd0ebbc7 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -989,18 +989,18 @@ module List = coll.AddMany(values) // insert values BEFORE the item at the index coll.AddManyAndClose(curr) - [] - let shuffleRand (random: Random) (source: 'T list) : 'T list = + [] + let randomShuffleWith (random: Random) (source: 'T list) : 'T list = let tempArray = toArray source Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray ofArray tempArray - [] - let shuffle (source: 'T list) : 'T list = - shuffleRand ThreadSafeRandom.Shared source + [] + let randomShuffle (source: 'T list) : 'T list = + randomShuffleWith ThreadSafeRandom.Shared source - [] - let choiceRand (random: Random) (source: 'T list) : 'T = + [] + let randomChoiceWith (random: Random) (source: 'T list) : 'T = let inputLength = source.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1008,12 +1008,12 @@ module List = let i = random.Next(0, inputLength) source.[i] - [] - let choice (source: 'T list) : 'T = - choiceRand ThreadSafeRandom.Shared source + [] + let randomChoice (source: 'T list) : 'T = + randomChoiceWith ThreadSafeRandom.Shared source - [] - let choicesRand (random: Random) (count: int) (source: 'T list) : 'T list = + [] + let randomChoicesWith (random: Random) (count: int) (source: 'T list) : 'T list = if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1027,12 +1027,12 @@ module List = source.[j] ] - [] - let choices (count: int) (source: 'T list) : 'T list = - choicesRand ThreadSafeRandom.Shared count source + [] + let randomChoices (count: int) (source: 'T list) : 'T list = + randomChoicesWith ThreadSafeRandom.Shared count source - [] - let sampleRand (random: Random) (count: int) (source: 'T list) : 'T list = + [] + let randomSampleWith (random: Random) (count: int) (source: 'T list) : 'T list = if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1067,6 +1067,6 @@ module List = source[j] ] - [] - let sample (count: int) (source: 'T list) : 'T list = - sampleRand ThreadSafeRandom.Shared count source + [] + let randomSample (count: int) (source: 'T list) : 'T list = + randomSampleWith ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index 3a47242e2d4..f2dde4e9ce3 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2695,26 +2695,57 @@ module List = [] val insertManyAt: index: int -> values: seq<'T> -> source: 'T list -> 'T list - [] - val shuffle : source: 'T list -> 'T list + /// Return a new list shuffled in a random order. + /// + /// The input list. + /// + /// The result list. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomShuffle + /// Can evaluate to [ 0; 2; 4; 3; 1 ]. + /// + /// + [] + val randomShuffle : source: 'T list -> 'T list - [] - val shuffleRand : random: Random -> source: 'T list -> 'T list + /// Return a new list shuffled in a random order with the specified Random instance. + /// + /// The Random instance. + /// The input list. + /// + /// The result list. + /// + /// Thrown when the random argument is null. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomShuffleWith Random.Shared + /// + /// Can evaluate to [ 0; 2; 4; 3; 1 ]. + /// + [] + val randomShuffleWith : random: Random -> source: 'T list -> 'T list - [] - val choice : source: 'T list -> 'T + [] + val randomChoice : source: 'T list -> 'T - [] - val choiceRand : random: Random -> source: 'T list -> 'T + [] + val randomChoiceWith : random: Random -> source: 'T list -> 'T - [] - val choices : count: int -> source: 'T list -> 'T list + [] + val randomChoices : count: int -> source: 'T list -> 'T list - [] - val choicesRand : random: Random -> count: int -> source: 'T list -> 'T list + [] + val randomChoicesWith : random: Random -> count: int -> source: 'T list -> 'T list - [] - val sample : count: int -> source: 'T list -> 'T list + [] + val randomSample : count: int -> source: 'T list -> 'T list - [] - val sampleRand : random: Random -> count: int -> source: 'T list -> 'T list \ No newline at end of file + [] + val randomSampleWith : random: Random -> count: int -> source: 'T list -> 'T list \ No newline at end of file diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 173be5ca183..e8dc3d30c66 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1939,18 +1939,18 @@ module Seq = invalidArg "index" "index must be within bounds of the array" } - [] - let shuffleRand (random: Random) (source: seq<'T>) : seq<'T> = + [] + let randomShuffleWith (random: Random) (source: seq<'T>) : seq<'T> = let tempArray = toArray source Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray ofArray tempArray - [] - let shuffle (source: seq<'T>) : seq<'T> = - shuffleRand ThreadSafeRandom.Shared source + [] + let randomShuffle (source: seq<'T>) : seq<'T> = + randomShuffleWith ThreadSafeRandom.Shared source - [] - let choiceRand (random: Random) (source: seq<'T>) : 'T = + [] + let randomChoiceWith (random: Random) (source: seq<'T>) : 'T = let inputLength = source |> length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1958,12 +1958,12 @@ module Seq = let i = random.Next(0, inputLength) source |> item i - [] - let choice (source: seq<'T>) : 'T = - choiceRand ThreadSafeRandom.Shared source + [] + let randomChoice (source: seq<'T>) : 'T = + randomChoiceWith ThreadSafeRandom.Shared source - [] - let choicesRand (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + [] + let randomChoicesWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1977,12 +1977,12 @@ module Seq = source |> item j } - [] - let choices (count: int) (source: seq<'T>) : seq<'T> = - choicesRand ThreadSafeRandom.Shared count source + [] + let randomChoices (count: int) (source: seq<'T>) : seq<'T> = + randomChoicesWith ThreadSafeRandom.Shared count source - [] - let sampleRand (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + [] + let randomSampleWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2017,6 +2017,6 @@ module Seq = source |> item j } - [] - let sample (count: int) (source: seq<'T>) : seq<'T> = - sampleRand ThreadSafeRandom.Shared count source + [] + let randomSample (count: int) (source: seq<'T>) : seq<'T> = + randomSampleWith ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index d14826a1729..32fd848baee 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2936,26 +2936,60 @@ module Seq = [] val insertManyAt: index: int -> values: seq<'T> -> source: seq<'T> -> seq<'T> - [] - val shuffle : source: seq<'T> -> seq<'T> + /// Return a new sequence shuffled a in random order. + /// + /// The input sequence. + /// + /// The result sequence. + /// + /// Thrown when the input sequence is null. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomShuffle + /// + /// Can evaluate to seq { 0; 2; 4; 3; 1 }. + /// + [] + val randomShuffle : source: seq<'T> -> seq<'T> - [] - val shuffleRand : random: Random -> source: seq<'T> -> seq<'T> + /// Return a new sequence shuffled in a random order with the specified Random instance. + /// + /// The Random instance. + /// The input sequence. + /// + /// The result sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the random argument is null. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomShuffleWith Random.Shared + /// + /// Can evaluate to seq { 0; 2; 4; 3; 1 }. + /// + [] + val randomShuffleWith : random: Random -> source: seq<'T> -> seq<'T> - [] - val choice : source: seq<'T> -> 'T + [] + val randomChoice : source: seq<'T> -> 'T - [] - val choiceRand : random: Random -> source: seq<'T> -> 'T + [] + val randomChoiceWith : random: Random -> source: seq<'T> -> 'T - [] - val choices : count: int -> source: seq<'T> -> seq<'T> + [] + val randomChoices : count: int -> source: seq<'T> -> seq<'T> - [] - val choicesRand : random: Random -> count: int -> source: seq<'T> -> seq<'T> + [] + val randomChoicesWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> - [] - val sample : count: int -> source: seq<'T> -> seq<'T> + [] + val randomSample : count: int -> source: seq<'T> -> seq<'T> - [] - val sampleRand : random: Random -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file + [] + val randomSampleWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 93535448985..71c95bccd26 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -1970,14 +1970,14 @@ type ArrayModule() = let mutable isShuffled = false let mutable i = 0 while not isShuffled && i < 3 do - let shuffled = arr |> Array.shuffle + let shuffled = arr |> Array.randomShuffle isShuffled <- not (arr = shuffled) i <- i + 1 Assert.NotEqual(3, i) // null array let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.shuffle nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomShuffle nullArr |> ignore) [] member _.ShuffleRand() = @@ -1986,9 +1986,9 @@ type ArrayModule() = let rand2 = Random(123) let rand3 = Random(321) - let shuffle1 = arr |> Array.shuffleRand rand1 - let shuffle2 = arr |> Array.shuffleRand rand2 - let shuffle3 = arr |> Array.shuffleRand rand3 + let shuffle1 = arr |> Array.randomShuffleWith rand1 + let shuffle2 = arr |> Array.randomShuffleWith rand2 + let shuffle3 = arr |> Array.randomShuffleWith rand3 Assert.AreEqual(shuffle1, shuffle2) Assert.AreNotEqual(arr, shuffle1) @@ -2000,23 +2000,23 @@ type ArrayModule() = // try choice six times, if all are same, it must be broken let results = [| - Array.choice arr - Array.choice arr - Array.choice arr - Array.choice arr - Array.choice arr - Array.choice arr + Array.randomChoice arr + Array.randomChoice arr + Array.randomChoice arr + Array.randomChoice arr + Array.randomChoice arr + Array.randomChoice arr |] let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) // null array let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.choice nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomChoice nullArr |> ignore) // empty array let emptyArr = [||] - CheckThrowsArgumentException (fun () -> Array.choice emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoice emptyArr |> ignore) [] member _.ChoiceRand() = @@ -2025,9 +2025,9 @@ type ArrayModule() = let rand2 = Random(123) let rand3 = Random(321) - let choice1 = arr |> Array.choiceRand rand1 - let choice2 = arr |> Array.choiceRand rand2 - let choice3 = arr |> Array.choiceRand rand3 + let choice1 = arr |> Array.randomChoiceWith rand1 + let choice2 = arr |> Array.randomChoiceWith rand2 + let choice3 = arr |> Array.randomChoiceWith rand3 Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -2039,9 +2039,9 @@ type ArrayModule() = // try choices three times, if all are same, it must be broken let choicesLength = 5 let results = [| - Array.choices choicesLength arr - Array.choices choicesLength arr - Array.choices choicesLength arr + Array.randomChoices choicesLength arr + Array.randomChoices choicesLength arr + Array.randomChoices choicesLength arr |] let allSame = results |> Array.forall (fun x -> x = results.[0]) let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) @@ -2050,15 +2050,15 @@ type ArrayModule() = // null array let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.choices choicesLength nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomChoices choicesLength nullArr |> ignore) // empty array let emptyArr = [||] - CheckThrowsArgumentException (fun () -> Array.choices choicesLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoices choicesLength emptyArr |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Array.choices negativeChoicesLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoices negativeChoicesLength arr |> ignore) [] member _.ChoicesRand() = @@ -2068,9 +2068,9 @@ type ArrayModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = arr |> Array.choicesRand rand1 choicesLength - let choice2 = arr |> Array.choicesRand rand2 choicesLength - let choice3 = arr |> Array.choicesRand rand3 choicesLength + let choice1 = arr |> Array.randomChoicesWith rand1 choicesLength + let choice2 = arr |> Array.randomChoicesWith rand2 choicesLength + let choice3 = arr |> Array.randomChoicesWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -2085,7 +2085,7 @@ type ArrayModule() = // try sample tryCount times, if all are same, it must be broken let results = [| for _ in 1..tryCount do - Array.sample choicesLength arr + Array.randomSample choicesLength arr |] let allSame = results |> Array.forall (fun x -> x = results.[0]) let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) @@ -2099,19 +2099,19 @@ type ArrayModule() = // null array let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.sample choicesLengthSmall nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomSample choicesLengthSmall nullArr |> ignore) // empty array let emptyArr = [||] - CheckThrowsArgumentException (fun () -> Array.sample choicesLengthSmall emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample choicesLengthSmall emptyArr |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Array.sample negativeChoicesLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample negativeChoicesLength arr |> ignore) // invalid count let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> Array.sample invalidCountRange arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample invalidCountRange arr |> ignore) [] member _.SampleRand() = @@ -2121,9 +2121,9 @@ type ArrayModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = arr |> Array.sampleRand rand1 choicesLength - let choice2 = arr |> Array.sampleRand rand2 choicesLength - let choice3 = arr |> Array.sampleRand rand3 choicesLength + let choice1 = arr |> Array.randomSample rand1 choicesLength + let choice2 = arr |> Array.randomSample rand2 choicesLength + let choice3 = arr |> Array.randomSample rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs index 46f8209b0dd..51104f80713 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs @@ -1096,7 +1096,7 @@ type ListModule() = let mutable isShuffled = false let mutable i = 0 while not isShuffled && i < 3 do - let shuffled = list |> List.shuffle + let shuffled = list |> List.randomShuffle isShuffled <- not (list = shuffled) i <- i + 1 Assert.NotEqual(3, i) @@ -1108,9 +1108,9 @@ type ListModule() = let rand2 = Random(123) let rand3 = Random(321) - let shuffle1 = list |> List.shuffleRand rand1 - let shuffle2 = list |> List.shuffleRand rand2 - let shuffle3 = list |> List.shuffleRand rand3 + let shuffle1 = list |> List.randomShuffleWith rand1 + let shuffle2 = list |> List.randomShuffleWith rand2 + let shuffle3 = list |> List.randomShuffleWith rand3 Assert.AreEqual(shuffle1, shuffle2) Assert.AreNotEqual(list, shuffle1) @@ -1122,19 +1122,19 @@ type ListModule() = // try choice six times, if all are same, it must be broken let results = [| - List.choice list - List.choice list - List.choice list - List.choice list - List.choice list - List.choice list + List.randomChoice list + List.randomChoice list + List.randomChoice list + List.randomChoice list + List.randomChoice list + List.randomChoice list |] let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) // empty list let emptyList = [] - CheckThrowsArgumentException (fun () -> List.choice emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoice emptyList |> ignore) [] member _.ChoiceRand() = @@ -1143,9 +1143,9 @@ type ListModule() = let rand2 = Random(123) let rand3 = Random(321) - let choice1 = list |> List.choiceRand rand1 - let choice2 = list |> List.choiceRand rand2 - let choice3 = list |> List.choiceRand rand3 + let choice1 = list |> List.randomChoiceWith rand1 + let choice2 = list |> List.randomChoiceWith rand2 + let choice3 = list |> List.randomChoiceWith rand3 Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -1157,9 +1157,9 @@ type ListModule() = // try choices three times, if all are same, it must be broken let choicesLength = 5 let results = [| - List.choices choicesLength list - List.choices choicesLength list - List.choices choicesLength list + List.randomChoices choicesLength list + List.randomChoices choicesLength list + List.randomChoices choicesLength list |] let allSame = results |> Array.forall (fun x -> x = results.[0]) let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) @@ -1168,11 +1168,11 @@ type ListModule() = // empty list let emptyList = [] - CheckThrowsArgumentException (fun () -> List.choices choicesLength emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoices choicesLength emptyList |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> List.choices negativeChoicesLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoices negativeChoicesLength list |> ignore) [] member _.ChoicesRand() = @@ -1182,9 +1182,9 @@ type ListModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = list |> List.choicesRand rand1 choicesLength - let choice2 = list |> List.choicesRand rand2 choicesLength - let choice3 = list |> List.choicesRand rand3 choicesLength + let choice1 = list |> List.randomChoicesWith rand1 choicesLength + let choice2 = list |> List.randomChoicesWith rand2 choicesLength + let choice3 = list |> List.randomChoicesWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -1199,7 +1199,7 @@ type ListModule() = // try sample tryCount times, if all are same, it must be broken let results = [| for _ in 1..tryCount do - List.sample choicesLength list + List.randomSample choicesLength list |] let allSame = results |> Array.forall (fun x -> x = results.[0]) let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) @@ -1213,15 +1213,15 @@ type ListModule() = // empty list let emptyList = [] - CheckThrowsArgumentException (fun () -> List.sample choicesLengthSmall emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSample choicesLengthSmall emptyList |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> List.sample negativeChoicesLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSample negativeChoicesLength list |> ignore) // invalid count let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> List.sample invalidCountRange list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSample invalidCountRange list |> ignore) [] member _.SampleRand() = @@ -1231,9 +1231,9 @@ type ListModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = list |> List.sampleRand rand1 choicesLength - let choice2 = list |> List.sampleRand rand2 choicesLength - let choice3 = list |> List.sampleRand rand3 choicesLength + let choice1 = list |> List.randomSampleWith rand1 choicesLength + let choice2 = list |> List.randomSampleWith rand2 choicesLength + let choice3 = list |> List.randomSampleWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index 5ca30b0d247..03c82a27ca6 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1169,7 +1169,7 @@ type SeqModule() = let mutable isShuffled = false let mutable i = 0 while not isShuffled && i < 3 do - let shuffled = seq |> Seq.shuffle + let shuffled = seq |> Seq.randomShuffle isShuffled <- not (seq = shuffled) i <- i + 1 Assert.NotEqual(3, i) @@ -1181,9 +1181,9 @@ type SeqModule() = let rand2 = Random(123) let rand3 = Random(321) - let shuffle1 = seq |> Seq.shuffleRand rand1 |> Seq.cache - let shuffle2 = seq |> Seq.shuffleRand rand2 |> Seq.cache - let shuffle3 = seq |> Seq.shuffleRand rand3 |> Seq.cache + let shuffle1 = seq |> Seq.randomShuffleWith rand1 |> Seq.cache + let shuffle2 = seq |> Seq.randomShuffleWith rand2 |> Seq.cache + let shuffle3 = seq |> Seq.randomShuffleWith rand3 |> Seq.cache Assert.AreEqual(shuffle1, shuffle2) Assert.AreNotEqual(seq, shuffle1) @@ -1195,19 +1195,19 @@ type SeqModule() = // try choice six times, if all are same, it must be broken let results = [| - Seq.choice seq - Seq.choice seq - Seq.choice seq - Seq.choice seq - Seq.choice seq - Seq.choice seq + Seq.randomChoice seq + Seq.randomChoice seq + Seq.randomChoice seq + Seq.randomChoice seq + Seq.randomChoice seq + Seq.randomChoice seq |] let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) // empty seq let emptySeq = Seq.empty - CheckThrowsArgumentException (fun () -> Seq.choice emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoice emptySeq |> ignore) [] member _.ChoiceRand() = @@ -1216,9 +1216,9 @@ type SeqModule() = let rand2 = Random(123) let rand3 = Random(321) - let choice1 = seq |> Seq.choiceRand rand1 - let choice2 = seq |> Seq.choiceRand rand2 - let choice3 = seq |> Seq.choiceRand rand3 + let choice1 = seq |> Seq.randomChoiceWith rand1 + let choice2 = seq |> Seq.randomChoiceWith rand2 + let choice3 = seq |> Seq.randomChoiceWith rand3 Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -1230,9 +1230,9 @@ type SeqModule() = // try choices three times, if all are same, it must be broken let choicesLength = 5 let results = [| - Seq.choices choicesLength seq |> Seq.cache - Seq.choices choicesLength seq |> Seq.cache - Seq.choices choicesLength seq |> Seq.cache + Seq.randomChoices choicesLength seq |> Seq.cache + Seq.randomChoices choicesLength seq |> Seq.cache + Seq.randomChoices choicesLength seq |> Seq.cache |] let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) @@ -1241,11 +1241,11 @@ type SeqModule() = // empty seq let emptySeq = Seq.empty - CheckThrowsArgumentException (fun () -> Seq.choices choicesLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoices choicesLength emptySeq |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Seq.choices negativeChoicesLength seq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoices negativeChoicesLength seq |> ignore) [] member _.ChoicesRand() = @@ -1255,9 +1255,9 @@ type SeqModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = seq |> Seq.choicesRand rand1 choicesLength - let choice2 = seq |> Seq.choicesRand rand2 choicesLength - let choice3 = seq |> Seq.choicesRand rand3 choicesLength + let choice1 = seq |> Seq.randomChoicesWith rand1 choicesLength + let choice2 = seq |> Seq.randomChoicesWith rand2 choicesLength + let choice3 = seq |> Seq.randomChoicesWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) @@ -1272,7 +1272,7 @@ type SeqModule() = // try sample tryCount times, if all are same, it must be broken let results = [| for _ in 1..tryCount do - Seq.sample choicesLength seq |> Seq.cache + Seq.randomSample choicesLength seq |> Seq.cache |] let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) @@ -1286,15 +1286,15 @@ type SeqModule() = // empty seq let emptySeq = Seq.empty - CheckThrowsArgumentException (fun () -> Seq.sample choicesLengthSmall emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample choicesLengthSmall emptySeq |> ignore) // negative choices length let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Seq.sample negativeChoicesLength seq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample negativeChoicesLength seq |> ignore) // invalid count let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> Seq.sample invalidCountRange seq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample invalidCountRange seq |> ignore) [] member _.SampleRand() = @@ -1304,9 +1304,9 @@ type SeqModule() = let rand3 = Random(321) let choicesLength = 5 - let choice1 = seq |> Seq.sampleRand rand1 choicesLength - let choice2 = seq |> Seq.sampleRand rand2 choicesLength - let choice3 = seq |> Seq.sampleRand rand3 choicesLength + let choice1 = seq |> Seq.randomSampleWith rand1 choicesLength + let choice2 = seq |> Seq.randomSampleWith rand2 choicesLength + let choice3 = seq |> Seq.randomSampleWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) \ No newline at end of file From ac6d5aaefaa27409ec06e6faa90f4643285e3496 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Thu, 30 May 2024 09:26:15 -0700 Subject: [PATCH 03/25] Random functions: More naming refactoring and documentation --- src/FSharp.Core/array.fs | 16 +-- src/FSharp.Core/array.fsi | 100 ++++++++++++++++-- src/FSharp.Core/list.fsi | 80 ++++++++++++++ src/FSharp.Core/seq.fsi | 84 +++++++++++++++ .../ArrayModule.fs | 22 ++-- .../ListModule.fs | 16 +-- .../Microsoft.FSharp.Collections/SeqModule.fs | 16 +-- 7 files changed, 291 insertions(+), 43 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 2fc2399a42b..df2ab7092b5 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1937,7 +1937,7 @@ module Array = result [] - let randomShuffleWith (random: Random) (source: 'T[]) : 'T[] = + let randomShuffleWith (random: Random) (source: 'T array) : 'T array = checkNonNull "source" source let result = copy source @@ -1947,11 +1947,11 @@ module Array = result [] - let randomShuffle (source: 'T[]) : 'T[] = + let randomShuffle (source: 'T array) : 'T array = randomShuffleWith ThreadSafeRandom.Shared source [] - let randomChoiceWith (random: Random) (source: 'T[]) : 'T = + let randomChoiceWith (random: Random) (source: 'T array) : 'T = checkNonNull "source" source let inputLength = source.Length @@ -1962,11 +1962,11 @@ module Array = source.[i] [] - let randomChoice (source: 'T[]) : 'T = + let randomChoice (source: 'T array) : 'T = randomChoiceWith ThreadSafeRandom.Shared source [] - let randomChoicesWith (random: Random) (count: int) (source: 'T[]) : 'T[] = + let randomChoicesWith (random: Random) (count: int) (source: 'T array) : 'T array = checkNonNull "source" source if count < 0 then @@ -1984,11 +1984,11 @@ module Array = result [] - let randomChoices (count: int) (source: 'T[]) : 'T[] = + let randomChoices (count: int) (source: 'T array) : 'T array = randomChoicesWith ThreadSafeRandom.Shared count source [] - let randomSampleWith (random: Random) (count: int) (source: 'T[]) : 'T[] = + let randomSampleWith (random: Random) (count: int) (source: 'T array) : 'T array = checkNonNull "source" source if count < 0 then @@ -2025,7 +2025,7 @@ module Array = result [] - let randomSample (count: int) (source: 'T[]) : 'T[] = + let randomSample (count: int) (source: 'T array) : 'T array = randomSampleWith ThreadSafeRandom.Shared count source module Parallel = diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 8bd25d15aa3..1725a72d7e4 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3112,7 +3112,7 @@ module Array = /// Can evaluate to [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffle : source: 'T[] -> 'T[] + val randomShuffle : source: 'T array -> 'T array /// Return a new array shuffled in a random order with the specified Random instance. /// @@ -3133,25 +3133,109 @@ module Array = /// Can evaluate to [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleWith : random: Random -> source: 'T[] -> 'T[] + val randomShuffleWith : random: Random -> source: 'T array -> 'T array + /// + /// Returns a random element from the given array. + /// + /// + /// The input array. + /// + /// A randomly selected element from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoice + /// + /// Can evaluate to 3. + /// [] - val randomChoice : source: 'T[] -> 'T + val randomChoice : source: 'T array -> 'T + /// + /// Returns a random element from the given array with the specified Random instance. + /// + /// + /// The Random instance. + /// The input array. + /// + /// A randomly selected element from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the random argument is null. + /// Thrown when the input array is empty. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoiceWith Random.Shared + /// + /// Can evaluate to 3. + /// [] - val randomChoiceWith : random: Random -> source: 'T[] -> 'T + val randomChoiceWith : random: Random -> source: 'T array -> 'T + /// + /// Returns an array of random elements from the given array. + /// + /// + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoices 3 + /// + /// Can evaluate to [| 3; 1; 3 |]. + /// [] - val randomChoices : count: int -> source: 'T[] -> 'T[] + val randomChoices : count: int -> source: 'T array -> 'T array + /// + /// Returns an array of random elements from the given array with the specified Random instance. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the random argument is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoicesWith Random.Shared 3 + /// + /// Can evaluate to [| 3; 1; 3 |]. + /// [] - val randomChoicesWith : random: Random -> count: int -> source: 'T[] -> 'T[] + val randomChoicesWith : random: Random -> count: int -> source: 'T array -> 'T array [] - val randomSample : count: int -> source: 'T[] -> 'T[] + val randomSample : count: int -> source: 'T array -> 'T array [] - val randomSampleWith : random: Random -> count: int -> source: 'T[] -> 'T[] + val randomSampleWith : random: Random -> count: int -> source: 'T array -> 'T array /// Provides parallel operations on arrays module Parallel = diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index f2dde4e9ce3..f1cf6474524 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2732,15 +2732,95 @@ module List = [] val randomShuffleWith : random: Random -> source: 'T list -> 'T list + /// + /// Returns a random element from the given list. + /// + /// + /// The input list. + /// + /// A randomly selected element from the input list. + /// + /// Thrown when the input list is empty. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomChoice + /// + /// Can evaluate to 3. + /// [] val randomChoice : source: 'T list -> 'T + /// + /// Returns a random element from the given list with the specified Random instance. + /// + /// + /// The Random instance. + /// The input list. + /// + /// A randomly selected element from the input list. + /// + /// Thrown when the random argument is null. + /// Thrown when the input list is empty. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomChoiceWith Random.Shared + /// + /// Can evaluate to 3. + /// [] val randomChoiceWith : random: Random -> source: 'T list -> 'T + /// + /// Returns a list of random elements from the given list. + /// + /// + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomChoices 3 + /// + /// Can evaluate to [ 3; 1; 3 ]. + /// [] val randomChoices : count: int -> source: 'T list -> 'T list + /// + /// Returns a list of random elements from the given list with the specified Random instance. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the random argument is null. + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> Array.randomChoicesWith Random.Shared 3 + /// + /// Can evaluate to [ 3; 1; 3 ]. + /// [] val randomChoicesWith : random: Random -> count: int -> source: 'T list -> 'T list diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 32fd848baee..c33a889b430 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2976,15 +2976,99 @@ module Seq = [] val randomShuffleWith : random: Random -> source: seq<'T> -> seq<'T> + /// + /// Returns a random element from the given sequence. + /// + /// + /// The input sequence. + /// + /// A randomly selected element from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomChoice + /// + /// Can evaluate to 3. + /// [] val randomChoice : source: seq<'T> -> 'T + /// + /// Returns a random element from the given sequence with the specified Random instance. + /// + /// + /// The Random instance. + /// The input sequence. + /// + /// A randomly selected element from the input array. + /// + /// Thrown when the input sequence is null. + /// Thrown when the random argument is null. + /// Thrown when the input sequence is empty. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomChoiceWith Random.Shared + /// + /// Can evaluate to 3. + /// [] val randomChoiceWith : random: Random -> source: seq<'T> -> 'T + /// + /// Returns an sequence of random elements from the given sequence. + /// + /// + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomChoices 3 + /// + /// Can evaluate to seq { 3; 1; 3 }. + /// [] val randomChoices : count: int -> source: seq<'T> -> seq<'T> + /// + /// Returns a sequence of random elements from the given sequence with the specified Random instance. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the random argument is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomChoicesWith Random.Shared 3 + /// + /// Can evaluate to seq { 3; 1; 3 }. + /// [] val randomChoicesWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 71c95bccd26..12c1935bc57 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -1963,7 +1963,7 @@ type ArrayModule() = [] - member _.Shuffle() = + member _.RandomShuffle() = let arr = [|1;2;3;4;5;6;7;8|] // try shuffle three times, if it doesn't shuffle, it must be broken @@ -1980,7 +1980,7 @@ type ArrayModule() = CheckThrowsArgumentNullException (fun () -> Array.randomShuffle nullArr |> ignore) [] - member _.ShuffleRand() = + member _.RandomShuffleWith() = let arr = [|1;2;3;4;5;6;7;8|] let rand1 = Random(123) let rand2 = Random(123) @@ -1995,7 +1995,7 @@ type ArrayModule() = Assert.AreNotEqual(shuffle1, shuffle3) [] - member _.Choice() = + member _.RandomChoice() = let arr = [|1;2;3;4;5;6;7;8|] // try choice six times, if all are same, it must be broken @@ -2019,7 +2019,7 @@ type ArrayModule() = CheckThrowsArgumentException (fun () -> Array.randomChoice emptyArr |> ignore) [] - member _.ChoiceRand() = + member _.RandomChoiceWith() = let arr = [|1;2;3;4;5;6;7;8|] let rand1 = Random(123) let rand2 = Random(123) @@ -2033,7 +2033,7 @@ type ArrayModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Choices() = + member _.RandomChoices() = let arr = [|1;2;3;4;5;6;7;8|] // try choices three times, if all are same, it must be broken @@ -2061,7 +2061,7 @@ type ArrayModule() = CheckThrowsArgumentException (fun () -> Array.randomChoices negativeChoicesLength arr |> ignore) [] - member _.ChoicesRand() = + member _.RandomChoicesWith() = let arr = [|1;2;3;4;5;6;7;8|] let rand1 = Random(123) let rand2 = Random(123) @@ -2076,7 +2076,7 @@ type ArrayModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Sample() = + member _.RandomSample() = let arr = Array.init 50 id let choicesLengthSmall = 1 let choicesLengthLarge = 49 @@ -2114,16 +2114,16 @@ type ArrayModule() = CheckThrowsArgumentException (fun () -> Array.randomSample invalidCountRange arr |> ignore) [] - member _.SampleRand() = + member _.RandomSampleWith() = let arr = [|1;2;3;4;5;6;7;8|] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) let choicesLength = 5 - let choice1 = arr |> Array.randomSample rand1 choicesLength - let choice2 = arr |> Array.randomSample rand2 choicesLength - let choice3 = arr |> Array.randomSample rand3 choicesLength + let choice1 = arr |> Array.randomSampleWith rand1 choicesLength + let choice2 = arr |> Array.randomSampleWith rand2 choicesLength + let choice3 = arr |> Array.randomSampleWith rand3 choicesLength Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs index 51104f80713..53239b17a0c 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs @@ -1089,7 +1089,7 @@ type ListModule() = Assert.AreEqual(list.[^1], 4) [] - member _.Shuffle() = + member _.RandomShuffle() = let list = [1;2;3;4;5;6;7;8] // try shuffle three times, if it doesn't shuffle, it must be broken @@ -1102,7 +1102,7 @@ type ListModule() = Assert.NotEqual(3, i) [] - member _.ShuffleRand() = + member _.RandomShuffleWith() = let list = [1;2;3;4;5;6;7;8] let rand1 = Random(123) let rand2 = Random(123) @@ -1117,7 +1117,7 @@ type ListModule() = Assert.AreNotEqual(shuffle1, shuffle3) [] - member _.Choice() = + member _.RandomChoice() = let list = [1;2;3;4;5;6;7;8] // try choice six times, if all are same, it must be broken @@ -1137,7 +1137,7 @@ type ListModule() = CheckThrowsArgumentException (fun () -> List.randomChoice emptyList |> ignore) [] - member _.ChoiceRand() = + member _.RandomChoiceWith() = let list = [1;2;3;4;5;6;7;8] let rand1 = Random(123) let rand2 = Random(123) @@ -1151,7 +1151,7 @@ type ListModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Choices() = + member _.RandomChoices() = let list = [1;2;3;4;5;6;7;8] // try choices three times, if all are same, it must be broken @@ -1175,7 +1175,7 @@ type ListModule() = CheckThrowsArgumentException (fun () -> List.randomChoices negativeChoicesLength list |> ignore) [] - member _.ChoicesRand() = + member _.RandomChoicesWith() = let list = [1;2;3;4;5;6;7;8] let rand1 = Random(123) let rand2 = Random(123) @@ -1190,7 +1190,7 @@ type ListModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Sample() = + member _.RandomSample() = let list = List.init 50 id let choicesLengthSmall = 1 let choicesLengthLarge = 49 @@ -1224,7 +1224,7 @@ type ListModule() = CheckThrowsArgumentException (fun () -> List.randomSample invalidCountRange list |> ignore) [] - member _.SampleRand() = + member _.RandomSampleWith() = let list = [1;2;3;4;5;6;7;8] let rand1 = Random(123) let rand2 = Random(123) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index 03c82a27ca6..cc318fe3e08 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1162,7 +1162,7 @@ type SeqModule() = CheckThrowsArgumentNullException (fun () -> Seq.contains 5 nullSeq |> ignore) [] - member _.Shuffle() = + member _.RandomShuffle() = let seq = seq {1;2;3;4;5;6;7;8} // try shuffle three times, if it doesn't shuffle, it must be broken @@ -1175,7 +1175,7 @@ type SeqModule() = Assert.NotEqual(3, i) [] - member _.ShuffleRand() = + member _.RandomShuffleWith() = let seq = seq {1;2;3;4;5;6;7;8} let rand1 = Random(123) let rand2 = Random(123) @@ -1190,7 +1190,7 @@ type SeqModule() = Assert.AreNotEqual(shuffle1, shuffle3) [] - member _.Choice() = + member _.RandomChoice() = let seq = seq {1;2;3;4;5;6;7;8} // try choice six times, if all are same, it must be broken @@ -1210,7 +1210,7 @@ type SeqModule() = CheckThrowsArgumentException (fun () -> Seq.randomChoice emptySeq |> ignore) [] - member _.ChoiceRand() = + member _.RandomChoiceWith() = let seq = seq {1;2;3;4;5;6;7;8} let rand1 = Random(123) let rand2 = Random(123) @@ -1224,7 +1224,7 @@ type SeqModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Choices() = + member _.RandomChoices() = let seq = seq {1;2;3;4;5;6;7;8} // try choices three times, if all are same, it must be broken @@ -1248,7 +1248,7 @@ type SeqModule() = CheckThrowsArgumentException (fun () -> Seq.randomChoices negativeChoicesLength seq |> ignore) [] - member _.ChoicesRand() = + member _.RandomChoicesWith() = let seq = seq {1;2;3;4;5;6;7;8} let rand1 = Random(123) let rand2 = Random(123) @@ -1263,7 +1263,7 @@ type SeqModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.Sample() = + member _.RandomSample() = let seq = Seq.init 50 id let choicesLengthSmall = 1 let choicesLengthLarge = 49 @@ -1297,7 +1297,7 @@ type SeqModule() = CheckThrowsArgumentException (fun () -> Seq.randomSample invalidCountRange seq |> ignore) [] - member _.SampleRand() = + member _.RandomSampleWith() = let seq = seq {1;2;3;4;5;6;7;8} let rand1 = Random(123) let rand2 = Random(123) From aebc5e62ee5fe7f25ff6db9dd0b045ecca12cb90 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Thu, 30 May 2024 17:30:28 -0700 Subject: [PATCH 04/25] Random functions: More documentation coments --- src/FSharp.Core/array.fsi | 50 +++++++++++++++++++++++++++++++++++++-- src/FSharp.Core/list.fsi | 49 ++++++++++++++++++++++++++++++++++++-- src/FSharp.Core/seq.fsi | 50 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 143 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 1725a72d7e4..34c2d7546e0 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3182,7 +3182,7 @@ module Array = val randomChoiceWith : random: Random -> source: 'T array -> 'T /// - /// Returns an array of random elements from the given array. + /// Returns an array of random elements from the given array, each element can be selected multiple times. /// /// /// The number of elements to return. @@ -3206,7 +3206,7 @@ module Array = val randomChoices : count: int -> source: 'T array -> 'T array /// - /// Returns an array of random elements from the given array with the specified Random instance. + /// Returns an array of random elements from the given array with the specified Random instance, each element can be selected multiple times. /// /// /// The Random instance. @@ -3231,9 +3231,55 @@ module Array = [] val randomChoicesWith : random: Random -> count: int -> source: 'T array -> 'T array + /// + /// Returns a random sample of elements from the given array, each element can be selected only once. + /// + /// + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input array. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomSample 3 + /// + /// Can evaluate to [| 3; 1; 2 |]. + /// [] val randomSample : count: int -> source: 'T array -> 'T array + /// + /// Returns a random sample of elements from the given array with the specified Random instance, each element can be selected only once. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the random argument is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input array. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomSampleWith Random.Shared 3 + /// + /// Can evaluate to [| 3; 1; 2 |]. + /// [] val randomSampleWith : random: Random -> count: int -> source: 'T array -> 'T array diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index f1cf6474524..95b9ee8a0d8 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2754,7 +2754,7 @@ module List = val randomChoice : source: 'T list -> 'T /// - /// Returns a random element from the given list with the specified Random instance. + /// Returns a random element from the given list with the specified Random instance, each element can be selected multiple times. /// /// /// The Random instance. @@ -2800,7 +2800,7 @@ module List = val randomChoices : count: int -> source: 'T list -> 'T list /// - /// Returns a list of random elements from the given list with the specified Random instance. + /// Returns a list of random elements from the given list with the specified Random instance, each element can be selected multiple times. /// /// /// The Random instance. @@ -2824,8 +2824,53 @@ module List = [] val randomChoicesWith : random: Random -> count: int -> source: 'T list -> 'T list + /// + /// Returns a random sample of elements from the given list, each element can be selected only once. + /// + /// + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input list. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomSample 3 + /// + /// Can evaluate to [ 3; 1; 2 ]. + /// [] val randomSample : count: int -> source: 'T list -> 'T list + /// + /// Returns a random sample of elements from the given list with the specified Random instance, each element can be selected only once. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the input list is null. + /// Thrown when the random argument is null. + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input list. + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomSampleWith Random.Shared 3 + /// + /// Can evaluate to [ 3; 1; 2 ]. + /// [] val randomSampleWith : random: Random -> count: int -> source: 'T list -> 'T list \ No newline at end of file diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index c33a889b430..8c844a335fb 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -3023,7 +3023,7 @@ module Seq = val randomChoiceWith : random: Random -> source: seq<'T> -> 'T /// - /// Returns an sequence of random elements from the given sequence. + /// Returns an sequence of random elements from the given sequence, each element can be selected multiple times. /// /// /// The number of elements to return. @@ -3047,7 +3047,7 @@ module Seq = val randomChoices : count: int -> source: seq<'T> -> seq<'T> /// - /// Returns a sequence of random elements from the given sequence with the specified Random instance. + /// Returns a sequence of random elements from the given sequence with the specified Random instance, each element can be selected multiple times. /// /// /// The Random instance. @@ -3072,8 +3072,54 @@ module Seq = [] val randomChoicesWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> + /// + /// Returns a random sample of elements from the given sequence, each element can be selected only once. + /// + /// + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input sequence. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomSample 3 + /// + /// Can evaluate to seq { 3; 1; 2 }. + /// [] val randomSample : count: int -> source: seq<'T> -> seq<'T> + /// + /// Returns a random sample of elements from the given sequence with the specified Random instance, each element can be selected only once. + /// + /// + /// The Random instance. + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the random argument is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input sequence. + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomSampleWith Random.Shared 3 + /// + /// Can evaluate to seq { 3; 1; 2 }. + /// [] val randomSampleWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file From 2f27590e5579bc51e06e1ca1e10a22e2823442e4 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Fri, 31 May 2024 12:32:22 -0700 Subject: [PATCH 05/25] Random functions: Added randomShuffleInPlace functions and docs --- src/FSharp.Core/array.fs | 15 +++++++++++++++ src/FSharp.Core/array.fsi | 38 ++++++++++++++++++++++++++++++++++++-- src/FSharp.Core/list.fsi | 5 ++--- src/FSharp.Core/seq.fsi | 4 ++-- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index df2ab7092b5..f000502a12c 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1939,6 +1939,7 @@ module Array = [] let randomShuffleWith (random: Random) (source: 'T array) : 'T array = checkNonNull "source" source + checkNonNull "random" random let result = copy source @@ -1950,9 +1951,21 @@ module Array = let randomShuffle (source: 'T array) : 'T array = randomShuffleWith ThreadSafeRandom.Shared source + [] + let randomShuffleInPlaceWith (random: Random) (source: 'T array) = + checkNonNull "source" source + checkNonNull "random" random + + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random source + + [] + let randomShuffleInPlace (source: 'T array) = + randomShuffleInPlaceWith ThreadSafeRandom.Shared source + [] let randomChoiceWith (random: Random) (source: 'T array) : 'T = checkNonNull "source" source + checkNonNull "random" random let inputLength = source.Length if inputLength = 0 then @@ -1968,6 +1981,7 @@ module Array = [] let randomChoicesWith (random: Random) (count: int) (source: 'T array) : 'T array = checkNonNull "source" source + checkNonNull "random" random if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1990,6 +2004,7 @@ module Array = [] let randomSampleWith (random: Random) (count: int) (source: 'T array) : 'T array = checkNonNull "source" source + checkNonNull "random" random if count < 0 then invalidArgInputMustBeNonNegative "count" count diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 34c2d7546e0..b6d0a48233c 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3135,6 +3135,40 @@ module Array = [] val randomShuffleWith : random: Random -> source: 'T array -> 'T array + /// Sorts input array in a random order by mutating the array in-place. + /// + /// The input array. + /// + /// Thrown when the input array is null. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffleInPlace + /// + /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffleInPlace : source: 'T array -> 'T array + + /// Sorts input array in a random order with the specified Random instance by mutating the array in-place. + /// + /// The input array. + /// + /// Thrown when the input array is null. + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffleInPlaceWith Random.Shared + /// + /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffleInPlaceWith : source: 'T array -> 'T array + /// /// Returns a random element from the given array. /// @@ -3243,7 +3277,7 @@ module Array = /// Thrown when the input array is null. /// Thrown when the input array is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input array. + /// Thrown when count is greater than the length of the input array. /// /// /// @@ -3270,7 +3304,7 @@ module Array = /// Thrown when the random argument is null. /// Thrown when the input array is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input array. + /// Thrown when count is greater than the length of the input array. /// /// /// diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index 95b9ee8a0d8..3a6480d1b85 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2835,7 +2835,7 @@ module List = /// /// Thrown when the input list is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input list. + /// Thrown when count is greater than the length of the input list. /// /// /// @@ -2858,11 +2858,10 @@ module List = /// /// A list of randomly selected elements from the input list. /// - /// Thrown when the input list is null. /// Thrown when the random argument is null. /// Thrown when the input list is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input list. + /// Thrown when count is greater than the length of the input list. /// /// /// diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 8c844a335fb..b67ebc7f689 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -3084,7 +3084,7 @@ module Seq = /// Thrown when the input sequence is null. /// Thrown when the input sequence is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input sequence. + /// Thrown when count is greater than the length of the input sequence. /// /// /// @@ -3111,7 +3111,7 @@ module Seq = /// Thrown when the random argument is null. /// Thrown when the input sequence is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input sequence. + /// Thrown when count is greater than the length of the input sequence. /// /// /// From 938646a363a28306f5668e7c7ca6f5735c2b24d8 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Fri, 31 May 2024 12:34:00 -0700 Subject: [PATCH 06/25] Random functions: refactoring --- src/FSharp.Core/array.fs | 4 ++-- src/FSharp.Core/list.fs | 2 +- src/FSharp.Core/local.fs | 2 +- src/FSharp.Core/local.fsi | 2 +- src/FSharp.Core/seq.fs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index f000502a12c..15539b48168 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1943,7 +1943,7 @@ module Array = let result = copy source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random result + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random result result @@ -1956,7 +1956,7 @@ module Array = checkNonNull "source" source checkNonNull "random" random - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random source + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random source [] let randomShuffleInPlace (source: 'T array) = diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 3e7bd0ebbc7..8dd537236e9 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -992,7 +992,7 @@ module List = [] let randomShuffleWith (random: Random) (source: 'T list) : 'T list = let tempArray = toArray source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray ofArray tempArray [] diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index ae1ecb8db32..1e87b20ed03 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1174,7 +1174,7 @@ module internal Array = startIndex <- startIndex + minChunkSize res - let shuffleInPlaceRand (random:Random) (array : array<'T>) = + let shuffleInPlace (random:Random) (array : array<'T>) = let inputLength = array.Length for i = 0 to inputLength - 2 do let j = random.Next(i, inputLength) diff --git a/src/FSharp.Core/local.fsi b/src/FSharp.Core/local.fsi index d025c803433..610d301a91d 100644 --- a/src/FSharp.Core/local.fsi +++ b/src/FSharp.Core/local.fsi @@ -120,7 +120,7 @@ module internal Array = val stableSortInPlace: array: 'T array -> unit when 'T: comparison - val shuffleInPlaceRand: random: Random -> array: 'T[] -> unit + val shuffleInPlace: random: Random -> array: 'T[] -> unit module internal Seq = val tryLastV: 'T seq -> 'T ValueOption diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index e8dc3d30c66..2a6e14a5958 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1942,7 +1942,7 @@ module Seq = [] let randomShuffleWith (random: Random) (source: seq<'T>) : seq<'T> = let tempArray = toArray source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlaceRand random tempArray + Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray ofArray tempArray [] From cae6305036c9b70c0321b915e5a15e56639fb6e1 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Fri, 31 May 2024 23:15:29 -0700 Subject: [PATCH 07/25] Added null checks --- src/FSharp.Core/list.fs | 11 +++++++++-- src/FSharp.Core/seq.fs | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 8dd537236e9..a4847dd5a44 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -991,6 +991,8 @@ module List = [] let randomShuffleWith (random: Random) (source: 'T list) : 'T list = + checkNonNull "random" random + let tempArray = toArray source Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray ofArray tempArray @@ -1001,6 +1003,8 @@ module List = [] let randomChoiceWith (random: Random) (source: 'T list) : 'T = + checkNonNull "random" random + let inputLength = source.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1014,6 +1018,8 @@ module List = [] let randomChoicesWith (random: Random) (count: int) (source: 'T list) : 'T list = + checkNonNull "random" random + if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1033,6 +1039,8 @@ module List = [] let randomSampleWith (random: Random) (count: int) (source: 'T list) : 'T list = + checkNonNull "random" random + if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1052,9 +1060,8 @@ module List = [ for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) - let nextItem = pool[j] + yield pool[j] pool[j] <- pool[inputLength - i - 1] - nextItem ] else let selected = HashSet() diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 2a6e14a5958..9f79f71e398 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1941,6 +1941,9 @@ module Seq = [] let randomShuffleWith (random: Random) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + checkNonNull "random" random + let tempArray = toArray source Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray ofArray tempArray @@ -1951,6 +1954,9 @@ module Seq = [] let randomChoiceWith (random: Random) (source: seq<'T>) : 'T = + checkNonNull "source" source + checkNonNull "random" random + let inputLength = source |> length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1964,6 +1970,9 @@ module Seq = [] let randomChoicesWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + checkNonNull "random" random + if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -1983,6 +1992,9 @@ module Seq = [] let randomSampleWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + checkNonNull "random" random + if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2002,9 +2014,8 @@ module Seq = seq { for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) - let nextItem = pool[j] + yield pool[j] pool[j] <- pool[inputLength - i - 1] - nextItem } else let selected = HashSet() From 3889a45486ec0f0ebf69d7cdf5a5ee48d3418ef8 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Sun, 2 Jun 2024 13:38:55 -0700 Subject: [PATCH 08/25] Added *by functions --- src/FSharp.Core/array.fs | 92 ++++++++++++++++++++++++-- src/FSharp.Core/array.fsi | 135 +++++++++++++++++++++++++++++++++----- src/FSharp.Core/list.fs | 73 +++++++++++++++++++-- src/FSharp.Core/list.fsi | 114 ++++++++++++++++++++++++++------ src/FSharp.Core/local.fs | 61 +++++++++++++---- src/FSharp.Core/local.fsi | 8 ++- src/FSharp.Core/seq.fs | 77 ++++++++++++++++++++-- src/FSharp.Core/seq.fsi | 100 +++++++++++++++++++++++++++- 8 files changed, 593 insertions(+), 67 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 15539b48168..cfbfd231382 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1943,7 +1943,17 @@ module Array = let result = copy source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random result + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random result + + result + + [] + let randomShuffleBy (randomizer: unit -> float) (source: 'T array) : 'T array = + checkNonNull "source" source + + let result = copy source + + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceBy randomizer result result @@ -1956,7 +1966,13 @@ module Array = checkNonNull "source" source checkNonNull "random" random - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random source + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random source + + [] + let randomShuffleInPlaceBy (randomizer: unit -> float) (source: 'T array) = + checkNonNull "source" source + + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceBy randomizer source [] let randomShuffleInPlace (source: 'T array) = @@ -1972,7 +1988,18 @@ module Array = invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString let i = random.Next(0, inputLength) - source.[i] + source[i] + + [] + let randomChoiceBy (randomizer: unit -> float) (source: 'T array) : 'T = + checkNonNull "source" source + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + let i = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + source[i] [] let randomChoice (source: 'T array) : 'T = @@ -1994,7 +2021,25 @@ module Array = for i = 0 to count - 1 do let j = random.Next(0, inputLength) - result.[i] <- source.[j] + result[i] <- source[j] + result + + [] + let randomChoicesBy (randomizer: unit -> float) (count: int) (source: 'T array) : 'T array = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count + + for i = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + result[i] <- source[j] result [] @@ -2019,9 +2064,7 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let mutable setSize = 21 - if count > 5 then - setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count if inputLength <= setSize then let pool = copy source for i = 0 to count - 1 do @@ -2039,6 +2082,41 @@ module Array = result + [] + let randomSampleBy (randomizer: unit -> float) (count: int) (source: 'T array) : 'T array = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then + let pool = copy source + for i = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - i + result[i] <- pool[j] + pool[j] <- pool[inputLength - i - 1] + else + let selected = HashSet() + for i = 0 to count - 1 do + let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do + j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore + result[i] <- source[j] + + result + [] let randomSample (count: int) (source: 'T array) : 'T array = randomSampleWith ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index b6d0a48233c..1cdfbb024df 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3135,6 +3135,27 @@ module Array = [] val randomShuffleWith : random: Random -> source: 'T array -> 'T array + /// Return a new array shuffled in a random order using the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input array. + /// + /// The result array. + /// + /// Thrown when the input array is null. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffleBy Random.Shared.NextDouble + /// + /// Can evaluate to [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffleBy : randomizer: (unit -> float) -> source: 'T array -> 'T array + /// Sorts input array in a random order by mutating the array in-place. /// /// The input array. @@ -3169,9 +3190,26 @@ module Array = [] val randomShuffleInPlaceWith : source: 'T array -> 'T array - /// - /// Returns a random element from the given array. - /// + /// Sorts input array in a random order using the specified randomizer function by mutating the array in-place. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input array. + /// + /// Thrown when the input array is null. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomShuffleInPlaceBy Random.Shared.NextDouble + /// + /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. + /// + [] + val randomShuffleInPlaceBy : randomizer: (unit -> float) -> source: 'T array -> 'T array + + /// Returns a random element from the given array. /// /// The input array. /// @@ -3191,9 +3229,7 @@ module Array = [] val randomChoice : source: 'T array -> 'T - /// - /// Returns a random element from the given array with the specified Random instance. - /// + /// Returns a random element from the given array with the specified Random instance. /// /// The Random instance. /// The input array. @@ -3215,9 +3251,29 @@ module Array = [] val randomChoiceWith : random: Random -> source: 'T array -> 'T - /// - /// Returns an array of random elements from the given array, each element can be selected multiple times. - /// + /// Returns a random element from the given array using the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input array. + /// + /// A randomly selected element from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoiceBy Random.Shared.NextDouble + /// + /// Can evaluate to 3. + /// + [] + val randomChoiceBy : randomizer: (unit -> float) -> source: 'T array -> 'T + + /// Returns an array of random elements from the given array, each element can be selected multiple times. /// /// The number of elements to return. /// The input array. @@ -3239,9 +3295,7 @@ module Array = [] val randomChoices : count: int -> source: 'T array -> 'T array - /// - /// Returns an array of random elements from the given array with the specified Random instance, each element can be selected multiple times. - /// + /// Returns an array of random elements from the given array with the specified Random instance, each element can be selected multiple times. /// /// The Random instance. /// The number of elements to return. @@ -3265,9 +3319,31 @@ module Array = [] val randomChoicesWith : random: Random -> count: int -> source: 'T array -> 'T array - /// - /// Returns a random sample of elements from the given array, each element can be selected only once. - /// + /// Returns an array of random elements from the given array using the specified randomizer function, each element can be selected multiple times. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomChoicesBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to [| 3; 1; 3 |]. + /// + [] + val randomChoicesBy : randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array + + /// Returns a random sample of elements from the given array, each element can be selected only once. /// /// The number of elements to return. /// The input array. @@ -3290,9 +3366,7 @@ module Array = [] val randomSample : count: int -> source: 'T array -> 'T array - /// - /// Returns a random sample of elements from the given array with the specified Random instance, each element can be selected only once. - /// + /// Returns a random sample of elements from the given array with the specified Random instance, each element can be selected only once. /// /// The Random instance. /// The number of elements to return. @@ -3317,6 +3391,31 @@ module Array = [] val randomSampleWith : random: Random -> count: int -> source: 'T array -> 'T array + /// Returns a random sample of elements from the given array using the specified randomizer function, each element can be selected only once. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input array. + /// + /// An array of randomly selected elements from the input array. + /// + /// Thrown when the input array is null. + /// Thrown when the input array is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input array. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [| 0; 1; 2; 3; 4 |] + /// + /// inputs |> Array.randomSampleBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to [| 3; 1; 2 |]. + /// + [] + val randomSampleBy : randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array + /// Provides parallel operations on arrays module Parallel = diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index a4847dd5a44..027fa731521 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -994,7 +994,13 @@ module List = checkNonNull "random" random let tempArray = toArray source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random tempArray + ofArray tempArray + + [] + let randomShuffleBy (randomizer: unit -> float) (source: 'T list) : 'T list = + let tempArray = toArray source + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceBy randomizer tempArray ofArray tempArray [] @@ -1010,7 +1016,16 @@ module List = invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString let i = random.Next(0, inputLength) - source.[i] + source[i] + + [] + let randomChoiceBy (randomizer: unit -> float) (source: 'T list) : 'T = + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + let i = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + source[i] [] let randomChoice (source: 'T list) : 'T = @@ -1030,7 +1045,22 @@ module List = [ for _ = 0 to count - 1 do let j = random.Next(0, inputLength) - source.[j] + source[j] + ] + + [] + let randomChoicesBy (randomizer: unit -> float) (count: int) (source: 'T list) : 'T list = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + [ + for _ = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + source[j] ] [] @@ -1052,9 +1082,7 @@ module List = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let mutable setSize = 21 - if count > 5 then - setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count if inputLength <= setSize then let pool = source |> toArray [ @@ -1074,6 +1102,39 @@ module List = source[j] ] + [] + let randomSampleBy (randomizer: unit -> float) (count: int) (source: 'T list) : 'T list = + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source.Length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then + let pool = source |> toArray + [ + for i = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + yield pool[j] + pool[j] <- pool[inputLength - i - 1] + ] + else + let selected = HashSet() + [ + for _ = 0 to count - 1 do + let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do + j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore + source[j] + ] + [] let randomSample (count: int) (source: 'T list) : 'T list = randomSampleWith ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index 3a6480d1b85..3a3839236cf 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2732,9 +2732,27 @@ module List = [] val randomShuffleWith : random: Random -> source: 'T list -> 'T list - /// - /// Returns a random element from the given list. - /// + /// Return a new list shuffled in a random order using the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input list. + /// + /// The result list. + /// + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomShuffleBy Random.Shared.NextDouble + /// + /// Can evaluate to [ 0; 2; 4; 3; 1 ]. + /// + [] + val randomShuffleBy : randomizer: (unit -> float) -> source: 'T list -> 'T list + + /// Returns a random element from the given list. /// /// The input list. /// @@ -2753,9 +2771,7 @@ module List = [] val randomChoice : source: 'T list -> 'T - /// - /// Returns a random element from the given list with the specified Random instance, each element can be selected multiple times. - /// + /// Returns a random element from the given list with the specified Random instance, each element can be selected multiple times. /// /// The Random instance. /// The input list. @@ -2776,9 +2792,28 @@ module List = [] val randomChoiceWith : random: Random -> source: 'T list -> 'T - /// - /// Returns a list of random elements from the given list. - /// + /// Returns a random element from the given list using the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input list. + /// + /// A randomly selected element from the input list. + /// + /// Thrown when the input list is empty. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomChoiceBy Random.Shared.NextDouble + /// + /// Can evaluate to 3. + /// + [] + val randomChoiceBy : randomizer: (unit -> float) -> source: 'T list -> 'T + + /// Returns a list of random elements from the given list. /// /// The number of elements to return. /// The input list. @@ -2799,9 +2834,7 @@ module List = [] val randomChoices : count: int -> source: 'T list -> 'T list - /// - /// Returns a list of random elements from the given list with the specified Random instance, each element can be selected multiple times. - /// + /// Returns a list of random elements from the given list with the specified Random instance, each element can be selected multiple times. /// /// The Random instance. /// The number of elements to return. @@ -2824,9 +2857,30 @@ module List = [] val randomChoicesWith : random: Random -> count: int -> source: 'T list -> 'T list - /// - /// Returns a random sample of elements from the given list, each element can be selected only once. - /// + /// Returns a list of random elements from the given list using the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomChoicesBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to [ 3; 1; 3 ]. + /// + [] + val randomChoicesBy : randomizer: (unit -> float) -> count: int -> source: 'T list -> 'T list + + /// Returns a random sample of elements from the given list, each element can be selected only once. /// /// The number of elements to return. /// The input list. @@ -2848,9 +2902,7 @@ module List = [] val randomSample : count: int -> source: 'T list -> 'T list - /// - /// Returns a random sample of elements from the given list with the specified Random instance, each element can be selected only once. - /// + /// Returns a random sample of elements from the given list with the specified Random instance, each element can be selected only once. /// /// The Random instance. /// The number of elements to return. @@ -2872,4 +2924,28 @@ module List = /// Can evaluate to [ 3; 1; 2 ]. /// [] - val randomSampleWith : random: Random -> count: int -> source: 'T list -> 'T list \ No newline at end of file + val randomSampleWith : random: Random -> count: int -> source: 'T list -> 'T list + + /// Returns a random sample of elements from the given list using the specified randomizer function, each element can be selected only once. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input list. + /// + /// A list of randomly selected elements from the input list. + /// + /// Thrown when the input list is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input list. + /// Thrown when the randomizer function returns a value outside the range [0, 1). + /// + /// + /// + /// let inputs = [ 0; 1; 2; 3; 4 ] + /// + /// inputs |> List.randomSampleBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to [ 3; 1; 2 ]. + /// + [] + val randomSampleBy : randomizer: (unit -> float) -> count: int -> source: 'T list -> 'T list \ No newline at end of file diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index 1e87b20ed03..5e111e0acb7 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -9,11 +9,17 @@ module internal DetailedExceptions = open System open Microsoft.FSharp.Core + /// takes an argument, a formatting string, a param array to splice into the formatting string let inline invalidArgFmt (arg:string) (format:string) paramArray = let msg = String.Format (format, paramArray) raise (new ArgumentException (msg, arg)) + /// takes an argument, a formatting string, a param array to splice into the formatting string + let inline invalidArgOufOfRangeFmt (arg:string) (format:string) paramArray = + let msg = String.Format (format, paramArray) + raise (new ArgumentOutOfRangeException (arg, msg)) + /// takes a formatting string and a param array to splice into the formatting string let inline invalidOpFmt (format:string) paramArray = let msg = String.Format (format, paramArray) @@ -1174,16 +1180,6 @@ module internal Array = startIndex <- startIndex + minChunkSize res - let shuffleInPlace (random:Random) (array : array<'T>) = - let inputLength = array.Length - for i = 0 to inputLength - 2 do - let j = random.Next(i, inputLength) - - if j <> i then - let temp = array[i] - array[i] <- array[j] - array[j] <- temp - module internal Seq = let tryLastV (source : seq<_>) = //checkNonNull "source" source //done in main Seq.tryLast @@ -1205,4 +1201,47 @@ module internal Seq = while (e.MoveNext()) do res <- e.Current ValueSome(res) else - ValueNone \ No newline at end of file + ValueNone + +module internal Random = + open System + + let private executeRandomizer (randomizer: unit -> float) = + let value = randomizer() + if value < 0.0 || value >= 1.0 then + let argName = nameof randomizer + invalidArgOufOfRangeFmt argName + "{0}\n{1} returned {2}, should be in range [0.0, 1.0)." + [|SR.GetString SR.outOfRange; argName; value|] + value + + let next (randomizer: unit -> float) (minVal: int) (maxVal: int) = + maxVal - minVal + |> float + |> (*) (executeRandomizer randomizer) + |> int + |> (+) minVal + + let shuffleArrayInPlaceWith (random: Random) (array: array<'T>) = + let inputLength = array.Length + for i = 0 to inputLength - 2 do + let j = random.Next(i, inputLength) + if j <> i then + let temp = array[i] + array[i] <- array[j] + array[j] <- temp + + let shuffleArrayInPlaceBy (randomizer: unit -> float) (array: array<'T>) = + let inputLength = array.Length + for i = 0 to inputLength - 2 do + let j = next randomizer i inputLength + if j <> i then + let temp = array[i] + array[i] <- array[j] + array[j] <- temp + + let getMaxSetSizeForSampling count = + let mutable setSize = 21 + if count > 5 then + setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + setSize \ No newline at end of file diff --git a/src/FSharp.Core/local.fsi b/src/FSharp.Core/local.fsi index 610d301a91d..e26035dd20f 100644 --- a/src/FSharp.Core/local.fsi +++ b/src/FSharp.Core/local.fsi @@ -120,7 +120,13 @@ module internal Array = val stableSortInPlace: array: 'T array -> unit when 'T: comparison - val shuffleInPlace: random: Random -> array: 'T[] -> unit +module internal Random = + + val next: randomizer: (unit -> float) -> minValue: int -> maxValue: int -> int + val getMaxSetSizeForSampling: count: int -> int + + val shuffleArrayInPlaceWith: random: Random -> array: 'T[] -> unit + val shuffleArrayInPlaceBy: random: (unit -> float) -> array: 'T[] -> unit module internal Seq = val tryLastV: 'T seq -> 'T ValueOption diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 9f79f71e398..1912cfe9573 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1945,7 +1945,15 @@ module Seq = checkNonNull "random" random let tempArray = toArray source - Microsoft.FSharp.Primitives.Basics.Array.shuffleInPlace random tempArray + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random tempArray + ofArray tempArray + + [] + let randomShuffleBy (randomizer: unit -> float) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + + let tempArray = toArray source + Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceBy randomizer tempArray ofArray tempArray [] @@ -1964,6 +1972,17 @@ module Seq = let i = random.Next(0, inputLength) source |> item i + [] + let randomChoiceBy (randomizer: unit -> float) (source: seq<'T>) : 'T = + checkNonNull "source" source + + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + let i = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + source |> item i + [] let randomChoice (source: seq<'T>) : 'T = randomChoiceWith ThreadSafeRandom.Shared source @@ -1986,6 +2005,23 @@ module Seq = source |> item j } + [] + let randomChoicesBy (randomizer: unit -> float) (count: int) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + seq { + for _ = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + source |> item j + } + [] let randomChoices (count: int) (source: seq<'T>) : seq<'T> = randomChoicesWith ThreadSafeRandom.Shared count source @@ -2006,9 +2042,7 @@ module Seq = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let mutable setSize = 21 - if count > 5 then - setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count if inputLength <= setSize then let pool = source |> toArray seq { @@ -2028,6 +2062,41 @@ module Seq = source |> item j } + [] + let randomSampleBy (randomizer: unit -> float) (count: int) (source: seq<'T>) : seq<'T> = + checkNonNull "source" source + + if count < 0 then + invalidArgInputMustBeNonNegative "count" count + + let inputLength = source |> length + if inputLength = 0 then + invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString + + if count >= inputLength then + invalidArgOutOfRange "count" count "source.Length" inputLength + + // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then + let pool = source |> toArray + seq { + for i = 0 to count - 1 do + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + yield pool[j] + pool[j] <- pool[inputLength - i - 1] + } + else + let selected = HashSet() + seq { + for _ = 0 to count - 1 do + let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do + j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore + source |> item j + } + [] let randomSample (count: int) (source: seq<'T>) : seq<'T> = randomSampleWith ThreadSafeRandom.Shared count source diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index b67ebc7f689..94b51c0483b 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2976,6 +2976,26 @@ module Seq = [] val randomShuffleWith : random: Random -> source: seq<'T> -> seq<'T> + /// Return a new sequence shuffled in a random order with the specified randomizer function. + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input sequence. + /// + /// The result sequence. + /// Thrown when the input sequence is null. + /// Thrown when the randomizer function returns a number outside the range [0.0..1.0). + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomShuffleBy Random.Shared.NextDouble + /// + /// Can evaluate to seq { 0; 2; 4; 3; 1 }. + /// + [] + val randomShuffleBy : randomizer: (unit -> float) -> source: seq<'T> -> seq<'T> + /// /// Returns a random element from the given sequence. /// @@ -3022,6 +3042,31 @@ module Seq = [] val randomChoiceWith : random: Random -> source: seq<'T> -> 'T + /// + /// Returns a random element from the given sequence with the specified randomizer function. + /// + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The input sequence. + /// + /// A randomly selected element from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// Thrown when the randomizer function returns a number outside the range [0.0..1.0). + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// let randomizer = Random.Shared.NextDouble + /// + /// inputs |> Seq.randomChoiceBy randomizer + /// + /// Can evaluate to 3. + /// + [] + val randomChoiceBy : randomizer: (unit -> float) -> source: seq<'T> -> 'T + /// /// Returns an sequence of random elements from the given sequence, each element can be selected multiple times. /// @@ -3072,6 +3117,32 @@ module Seq = [] val randomChoicesWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> + /// + /// Returns a sequence of random elements from the given sequence with the specified randomizer function, each element can be selected multiple times. + /// + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// Thrown when the randomizer function returns a number outside the range [0.0..1.0). + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomChoicesBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to seq { 3; 1; 3 }. + /// + [] + val randomChoicesBy : randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> + /// /// Returns a random sample of elements from the given sequence, each element can be selected only once. /// @@ -3122,4 +3193,31 @@ module Seq = /// Can evaluate to seq { 3; 1; 2 }. /// [] - val randomSampleWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file + val randomSampleWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> + + /// + /// Returns a random sample of elements from the given sequence with the specified randomizer function, each element can be selected only once. + /// + /// + /// The randomizer function, must return a float number from [0.0..1.0) range. + /// The number of elements to return. + /// The input sequence. + /// + /// A sequence of randomly selected elements from the input sequence. + /// + /// Thrown when the input sequence is null. + /// Thrown when the input sequence is empty. + /// Thrown when count is less than 0. + /// Thrown when count is greater than the length of the input sequence. + /// Thrown when the randomizer function returns a number outside the range [0.0..1.0). + /// + /// + /// + /// let inputs = seq { 0; 1; 2; 3; 4 } + /// + /// inputs |> Seq.randomSampleBy Random.Shared.NextDouble 3 + /// + /// Can evaluate to seq { 3; 1; 2 }. + /// + [] + val randomSampleBy : randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file From 73884e5fd7e6b338287a244a399410dea72957cb Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Mon, 3 Jun 2024 16:45:30 -0700 Subject: [PATCH 09/25] Random functions: Array random functions tests --- src/FSharp.Core/array.fs | 2 +- src/FSharp.Core/array.fsi | 7 +- src/FSharp.Core/local.fs | 6 +- src/FSharp.Core/local.fsi | 2 +- .../ArrayModule.fs | 242 ++++++++++++------ 5 files changed, 178 insertions(+), 81 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index cfbfd231382..65fe47a723a 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2058,7 +2058,7 @@ module Array = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString - if count >= inputLength then + if count > inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 1cdfbb024df..adf742ca104 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3171,11 +3171,12 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlace : source: 'T array -> 'T array + val randomShuffleInPlace : source: 'T array -> unit /// Sorts input array in a random order with the specified Random instance by mutating the array in-place. /// /// The input array. + /// The Random instance. /// /// Thrown when the input array is null. /// @@ -3188,7 +3189,7 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlaceWith : source: 'T array -> 'T array + val randomShuffleInPlaceWith : random: Random -> source: 'T array -> unit /// Sorts input array in a random order using the specified randomizer function by mutating the array in-place. /// @@ -3207,7 +3208,7 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlaceBy : randomizer: (unit -> float) -> source: 'T array -> 'T array + val randomShuffleInPlaceBy : randomizer: (unit -> float) -> source: 'T array -> unit /// Returns a random element from the given array. /// diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index 5e111e0acb7..c61e4961394 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1215,12 +1215,12 @@ module internal Random = [|SR.GetString SR.outOfRange; argName; value|] value - let next (randomizer: unit -> float) (minVal: int) (maxVal: int) = - maxVal - minVal + let next (randomizer: unit -> float) (minValue: int) (maxValue: int) = + maxValue - minValue |> float |> (*) (executeRandomizer randomizer) |> int - |> (+) minVal + |> (+) minValue let shuffleArrayInPlaceWith (random: Random) (array: array<'T>) = let inputLength = array.Length diff --git a/src/FSharp.Core/local.fsi b/src/FSharp.Core/local.fsi index e26035dd20f..9de6b8c8152 100644 --- a/src/FSharp.Core/local.fsi +++ b/src/FSharp.Core/local.fsi @@ -126,7 +126,7 @@ module internal Random = val getMaxSetSizeForSampling: count: int -> int val shuffleArrayInPlaceWith: random: Random -> array: 'T[] -> unit - val shuffleArrayInPlaceBy: random: (unit -> float) -> array: 'T[] -> unit + val shuffleArrayInPlaceBy: randomizer: (unit -> float) -> array: 'T[] -> unit module internal Seq = val tryLastV: 'T seq -> 'T ValueOption diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 12c1935bc57..291c289df91 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -1964,24 +1964,23 @@ type ArrayModule() = [] member _.RandomShuffle() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..20 |] - // try shuffle three times, if it doesn't shuffle, it must be broken - let mutable isShuffled = false - let mutable i = 0 - while not isShuffled && i < 3 do - let shuffled = arr |> Array.randomShuffle - isShuffled <- not (arr = shuffled) - i <- i + 1 - Assert.NotEqual(3, i) + let shuffled1 = arr |> Array.randomShuffle + let shuffled2 = arr |> Array.randomShuffle - // null array + Assert.AreNotEqual(shuffled1, arr) + Assert.AreNotEqual(shuffled1, shuffled2) + + [] + member _.RandomShuffleWrongArg() = let nullArr = null CheckThrowsArgumentNullException (fun () -> Array.randomShuffle nullArr |> ignore) [] member _.RandomShuffleWith() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..20 |] + let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) @@ -1994,33 +1993,91 @@ type ArrayModule() = Assert.AreNotEqual(arr, shuffle1) Assert.AreNotEqual(shuffle1, shuffle3) + [] + member _.RandomShuffleWithWrongArg() = + let nullArr = null + let arr = [| 1..20 |] + let nullRand = null + let rand = Random(123) + + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleWith rand nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleWith nullRand arr |> ignore) + + [] + member _.RandomShuffleInPlace() = + let arr = [| 1..20 |] + let shuffled1 = [| 1..20 |] + let shuffled2 = [| 1..20 |] + + shuffled1 |> Array.randomShuffleInPlace + shuffled2 |> Array.randomShuffleInPlace + + Assert.AreNotEqual(shuffled1, arr) + Assert.AreNotEqual(shuffled1, shuffled2) + + [] + member _.RandomShuffleInPlaceWrongArg() = + let nullArr = null + + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlace nullArr |> ignore) + + [] + member _.RandomShuffleInPlaceWith() = + let arr = [| 1..20 |] + + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = [| 1..20 |] + let shuffle2 = [| 1..20 |] + let shuffle3 = [| 1..20 |] + + shuffle1 |> Array.randomShuffleInPlaceWith rand1 + shuffle2 |> Array.randomShuffleInPlaceWith rand2 + shuffle3 |> Array.randomShuffleInPlaceWith rand3 + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(arr, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.RandomShuffleInPlaceWithWrongArg() = + let nullArr = null + let arr = [| 1..20 |] + let rand = Random(123) + let nullRand = null + + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceWith rand nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceWith nullRand arr |> ignore) + [] member _.RandomChoice() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..5000 |] - // try choice six times, if all are same, it must be broken + // try choice five times, if all are same, it must be broken let results = [| Array.randomChoice arr Array.randomChoice arr Array.randomChoice arr Array.randomChoice arr Array.randomChoice arr - Array.randomChoice arr |] let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) - // null array - let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.randomChoice nullArr |> ignore) - // empty array + [] + member _.RandomChoiceWrongArg() = + let nullArr = null let emptyArr = [||] + + CheckThrowsArgumentNullException (fun () -> Array.randomChoice nullArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomChoice emptyArr |> ignore) [] member _.RandomChoiceWith() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..5000 |] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) @@ -2032,42 +2089,55 @@ type ArrayModule() = Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) + [] + member _.RandomChoiceWithWrongArg() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..20 |] + let nullRand = null + let rand = Random(123) + + CheckThrowsArgumentNullException (fun () -> Array.randomChoiceWith rand nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomChoiceWith nullRand arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoiceWith rand emptyArr |> ignore) + [] member _.RandomChoices() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..50 |] - // try choices three times, if all are same, it must be broken - let choicesLength = 5 - let results = [| - Array.randomChoices choicesLength arr - Array.randomChoices choicesLength arr - Array.randomChoices choicesLength arr - |] - let allSame = results |> Array.forall (fun x -> x = results.[0]) - let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) - Assert.False(allSame) - Assert.True(allCorrectLength) + let choicesLength = 20 + let choice1 = arr |> Array.randomChoices choicesLength + let choice2 = arr |> Array.randomChoices choicesLength - // null array - let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.randomChoices choicesLength nullArr |> ignore) + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice2.Length) - // empty array - let emptyArr = [||] - CheckThrowsArgumentException (fun () -> Array.randomChoices choicesLength emptyArr |> ignore) + let arr = [| 1; 2 |] + let choices = arr |> Array.randomChoices choicesLength + Assert.AreEqual(choicesLength, choices.Length) + Assert.AreEqual(arr, choices |> Array.distinct) - // negative choices length + [] + member _.RandomChoicesNegative() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..50 |] + let choicesLength = 20 let negativeChoicesLength = -1 + + CheckThrowsArgumentNullException (fun () -> Array.randomChoices choicesLength nullArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoices choicesLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomChoices negativeChoicesLength arr |> ignore) [] member _.RandomChoicesWith() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..50 |] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 + let choicesLength = 20 let choice1 = arr |> Array.randomChoicesWith rand1 choicesLength let choice2 = arr |> Array.randomChoicesWith rand2 choicesLength let choice3 = arr |> Array.randomChoicesWith rand3 choicesLength @@ -2075,55 +2145,81 @@ type ArrayModule() = Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) + [] + member _.RandomChoicesWithWrongArg() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..50 |] + let nullRand = null + let rand = Random(123) + let choicesLength = 20 + let negativeChoicesLength = -1 + + CheckThrowsArgumentNullException (fun () -> Array.randomChoicesWith rand choicesLength nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomChoicesWith nullRand choicesLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoicesWith rand choicesLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoicesWith rand negativeChoicesLength arr |> ignore) + [] member _.RandomSample() = - let arr = Array.init 50 id - let choicesLengthSmall = 1 - let choicesLengthLarge = 49 - - let verify choicesLength tryCount = - // try sample tryCount times, if all are same, it must be broken - let results = [| - for _ in 1..tryCount do - Array.randomSample choicesLength arr - |] - let allSame = results |> Array.forall (fun x -> x = results.[0]) - let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) - let allDistinct = results |> Array.forall (fun x -> x = (x |> Array.distinct)) - Assert.False(allSame) - Assert.True(allCorrectLength) - Assert.True(allDistinct) + let arr = [| 1..50 |] - verify choicesLengthSmall 10 - verify choicesLengthLarge 2 + let choicesLength = 20 + let choice1 = arr |> Array.randomSample choicesLength + let choice2 = arr |> Array.randomSample choicesLength - // null array - let nullArr = null - CheckThrowsArgumentNullException (fun () -> Array.randomSample choicesLengthSmall nullArr |> ignore) + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice2.Length) + Assert.AreEqual(choice1, choice1 |> Array.distinct) + Assert.AreEqual(choice2, choice2 |> Array.distinct) - // empty array + [] + member _.RandomSampleNegative() = + let nullArr = null let emptyArr = [||] - CheckThrowsArgumentException (fun () -> Array.randomSample choicesLengthSmall emptyArr |> ignore) - - // negative choices length - let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Array.randomSample negativeChoicesLength arr |> ignore) + let arr = [| 1..50 |] + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 - // invalid count - let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> Array.randomSample invalidCountRange arr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomSample sampleLength nullArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample sampleLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample negativeSampleLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSample tooBigSampleLength arr |> ignore) [] member _.RandomSampleWith() = - let arr = [|1;2;3;4;5;6;7;8|] + let arr = [| 1..50 |] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 + let choicesLength = 20 let choice1 = arr |> Array.randomSampleWith rand1 choicesLength let choice2 = arr |> Array.randomSampleWith rand2 choicesLength let choice3 = arr |> Array.randomSampleWith rand3 choicesLength Assert.AreEqual(choice1, choice2) - Assert.AreNotEqual(choice1, choice3) \ No newline at end of file + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice3.Length) + Assert.AreEqual(choice1, choice1 |> Array.distinct) + Assert.AreEqual(choice3, choice3 |> Array.distinct) + + [] + member _.RandomSampleWithNegative() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..50 |] + let nullRand = null + let rand = Random(123) + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentNullException (fun () -> Array.randomSampleWith rand sampleLength nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> Array.randomSampleWith nullRand sampleLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand sampleLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand negativeSampleLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand tooBigSampleLength arr |> ignore) \ No newline at end of file From 6544ca42a1d723bc9c736df84857f3220a5dea06 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Mon, 3 Jun 2024 19:08:22 -0700 Subject: [PATCH 10/25] Added tests for lists and seqs --- .../ArrayModule.fs | 9 +- .../ListModule.fs | 190 +++++++++------ .../Microsoft.FSharp.Collections/SeqModule.fs | 230 +++++++++++------- 3 files changed, 268 insertions(+), 161 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 291c289df91..037279c5ae3 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -2066,7 +2066,6 @@ type ArrayModule() = let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) - [] member _.RandomChoiceWrongArg() = let nullArr = null @@ -2116,10 +2115,10 @@ type ArrayModule() = let arr = [| 1; 2 |] let choices = arr |> Array.randomChoices choicesLength Assert.AreEqual(choicesLength, choices.Length) - Assert.AreEqual(arr, choices |> Array.distinct) + Assert.AreEqual(arr, choices |> Array.distinct |> Array.sort) [] - member _.RandomChoicesNegative() = + member _.RandomChoicesWrongArg() = let nullArr = null let emptyArr = [||] let arr = [| 1..50 |] @@ -2175,7 +2174,7 @@ type ArrayModule() = Assert.AreEqual(choice2, choice2 |> Array.distinct) [] - member _.RandomSampleNegative() = + member _.RandomSampleWrongArg() = let nullArr = null let emptyArr = [||] let arr = [| 1..50 |] @@ -2208,7 +2207,7 @@ type ArrayModule() = Assert.AreEqual(choice3, choice3 |> Array.distinct) [] - member _.RandomSampleWithNegative() = + member _.RandomSampleWithWrongArg() = let nullArr = null let emptyArr = [||] let arr = [| 1..50 |] diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs index 53239b17a0c..63840ba45c1 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs @@ -1090,55 +1090,61 @@ type ListModule() = [] member _.RandomShuffle() = - let list = [1;2;3;4;5;6;7;8] + let list = [ 1..20 ] - // try shuffle three times, if it doesn't shuffle, it must be broken - let mutable isShuffled = false - let mutable i = 0 - while not isShuffled && i < 3 do - let shuffled = list |> List.randomShuffle - isShuffled <- not (list = shuffled) - i <- i + 1 - Assert.NotEqual(3, i) + let shuffled1 = list |> List.randomShuffle + let shuffled2 = list |> List.randomShuffle + + Assert.AreNotEqual(shuffled1, list) + Assert.AreNotEqual(shuffled1, shuffled2) [] member _.RandomShuffleWith() = - let list = [1;2;3;4;5;6;7;8] + let arr = [ 1..20 ] + let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let shuffle1 = list |> List.randomShuffleWith rand1 - let shuffle2 = list |> List.randomShuffleWith rand2 - let shuffle3 = list |> List.randomShuffleWith rand3 + let shuffle1 = arr |> List.randomShuffleWith rand1 + let shuffle2 = arr |> List.randomShuffleWith rand2 + let shuffle3 = arr |> List.randomShuffleWith rand3 Assert.AreEqual(shuffle1, shuffle2) - Assert.AreNotEqual(list, shuffle1) + Assert.AreNotEqual(arr, shuffle1) Assert.AreNotEqual(shuffle1, shuffle3) + [] + member _.RandomShuffleWithWrongArg() = + let list = [ 1..20 ] + let nullRand = null + + CheckThrowsArgumentNullException (fun () -> List.randomShuffleWith nullRand list |> ignore) + [] member _.RandomChoice() = - let list = [1;2;3;4;5;6;7;8] + let list = [ 1..5000 ] - // try choice six times, if all are same, it must be broken - let results = [| + // try choice five times, if all are same, it must be broken + let results = [ List.randomChoice list List.randomChoice list List.randomChoice list List.randomChoice list List.randomChoice list - List.randomChoice list - |] - let allSame = results |> Array.forall (fun x -> x = results.[0]) + ] + let allSame = results |> List.forall (fun x -> x = results.Head) Assert.False(allSame) - // empty list + [] + member _.RandomChoiceWrongArg() = let emptyList = [] + CheckThrowsArgumentException (fun () -> List.randomChoice emptyList |> ignore) [] member _.RandomChoiceWith() = - let list = [1;2;3;4;5;6;7;8] + let list = [ 1..5000 ] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) @@ -1150,38 +1156,51 @@ type ListModule() = Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) + [] + member _.RandomChoiceWithWrongArg() = + let emptyList = [] + let list = [ 1..20 ] + let nullRand = null + let rand = Random(123) + + CheckThrowsArgumentNullException (fun () -> List.randomChoiceWith nullRand list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoiceWith rand emptyList |> ignore) + [] member _.RandomChoices() = - let list = [1;2;3;4;5;6;7;8] - - // try choices three times, if all are same, it must be broken - let choicesLength = 5 - let results = [| - List.randomChoices choicesLength list - List.randomChoices choicesLength list - List.randomChoices choicesLength list - |] - let allSame = results |> Array.forall (fun x -> x = results.[0]) - let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) - Assert.False(allSame) - Assert.True(allCorrectLength) + let list = [ 1..50 ] - // empty list - let emptyList = [] - CheckThrowsArgumentException (fun () -> List.randomChoices choicesLength emptyList |> ignore) + let choicesLength = 20 + let choice1 = list |> List.randomChoices choicesLength + let choice2 = list |> List.randomChoices choicesLength + + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice2.Length) + + let list = [ 1; 2 ] + let choices = list |> List.randomChoices choicesLength + Assert.AreEqual(choicesLength, choices.Length) + Assert.AreEqual(list, choices |> List.distinct |> List.sort) - // negative choices length + [] + member _.RandomChoicesWrongArg() = + let emptyList = [] + let list = [ 1..50 ] + let choicesLength = 20 let negativeChoicesLength = -1 + + CheckThrowsArgumentException (fun () -> List.randomChoices choicesLength emptyList |> ignore) CheckThrowsArgumentException (fun () -> List.randomChoices negativeChoicesLength list |> ignore) [] member _.RandomChoicesWith() = - let list = [1;2;3;4;5;6;7;8] + let list = [ 1..50 ] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 + let choicesLength = 20 let choice1 = list |> List.randomChoicesWith rand1 choicesLength let choice2 = list |> List.randomChoicesWith rand2 choicesLength let choice3 = list |> List.randomChoicesWith rand3 choicesLength @@ -1190,50 +1209,75 @@ type ListModule() = Assert.AreNotEqual(choice1, choice3) [] - member _.RandomSample() = - let list = List.init 50 id - let choicesLengthSmall = 1 - let choicesLengthLarge = 49 - - let verify choicesLength tryCount = - // try sample tryCount times, if all are same, it must be broken - let results = [| - for _ in 1..tryCount do - List.randomSample choicesLength list - |] - let allSame = results |> Array.forall (fun x -> x = results.[0]) - let allCorrectLength = results |> Array.forall (fun x -> x.Length = choicesLength) - let allDistinct = results |> Array.forall (fun x -> x = (x |> List.distinct)) - Assert.False(allSame) - Assert.True(allCorrectLength) - Assert.True(allDistinct) - - verify choicesLengthSmall 10 - verify choicesLengthLarge 2 - - // empty list + member _.RandomChoicesWithWrongArg() = let emptyList = [] - CheckThrowsArgumentException (fun () -> List.randomSample choicesLengthSmall emptyList |> ignore) - - // negative choices length + let list = [ 1..50 ] + let nullRand = null + let rand = Random(123) + let choicesLength = 20 let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> List.randomSample negativeChoicesLength list |> ignore) - // invalid count - let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> List.randomSample invalidCountRange list |> ignore) + CheckThrowsArgumentNullException (fun () -> List.randomChoicesWith nullRand choicesLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoicesWith rand choicesLength emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoicesWith rand negativeChoicesLength list |> ignore) + + [] + member _.RandomSample() = + let arr = [ 1..50 ] + + let choicesLength = 20 + let choice1 = arr |> List.randomSample choicesLength + let choice2 = arr |> List.randomSample choicesLength + + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice2.Length) + Assert.AreEqual(choice1, choice1 |> List.distinct) + Assert.AreEqual(choice2, choice2 |> List.distinct) + + [] + member _.RandomSampleWrongArg() = + let emptyList = [] + let list = [ 1..50 ] + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentException (fun () -> List.randomSample sampleLength emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSample negativeSampleLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSample tooBigSampleLength list |> ignore) + [] member _.RandomSampleWith() = - let list = [1;2;3;4;5;6;7;8] + let list = [ 1..50 ] let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 + let choicesLength = 20 let choice1 = list |> List.randomSampleWith rand1 choicesLength let choice2 = list |> List.randomSampleWith rand2 choicesLength let choice3 = list |> List.randomSampleWith rand3 choicesLength Assert.AreEqual(choice1, choice2) - Assert.AreNotEqual(choice1, choice3) \ No newline at end of file + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice3.Length) + Assert.AreEqual(choice1, choice1 |> List.distinct) + Assert.AreEqual(choice3, choice3 |> List.distinct) + + [] + member _.RandomSampleWithWrongArg() = + let emptyArr = [] + let list = [ 1..50 ] + let nullRand = null + let rand = Random(123) + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentNullException (fun () -> List.randomSampleWith nullRand sampleLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleWith rand sampleLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleWith rand negativeSampleLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleWith rand tooBigSampleLength list |> ignore) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index cc318fe3e08..8e89a6b451f 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1163,150 +1163,214 @@ type SeqModule() = [] member _.RandomShuffle() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..20 } - // try shuffle three times, if it doesn't shuffle, it must be broken - let mutable isShuffled = false - let mutable i = 0 - while not isShuffled && i < 3 do - let shuffled = seq |> Seq.randomShuffle - isShuffled <- not (seq = shuffled) - i <- i + 1 - Assert.NotEqual(3, i) + let shuffled1 = intSeq |> Seq.randomShuffle |> Seq.cache + let shuffled2 = intSeq |> Seq.randomShuffle |> Seq.cache + + Assert.AreNotEqual(shuffled1, intSeq) + Assert.AreNotEqual(shuffled1, shuffled2) + + [] + member _.RandomShuffleWrongArg() = + let nullSeq = null + CheckThrowsArgumentNullException (fun () -> Seq.randomShuffle nullSeq |> ignore) [] member _.RandomShuffleWith() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..20 } + let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let shuffle1 = seq |> Seq.randomShuffleWith rand1 |> Seq.cache - let shuffle2 = seq |> Seq.randomShuffleWith rand2 |> Seq.cache - let shuffle3 = seq |> Seq.randomShuffleWith rand3 |> Seq.cache + let shuffle1 = intSeq |> Seq.randomShuffleWith rand1 |> Seq.cache + let shuffle2 = intSeq |> Seq.randomShuffleWith rand2 |> Seq.cache + let shuffle3 = intSeq |> Seq.randomShuffleWith rand3 |> Seq.cache Assert.AreEqual(shuffle1, shuffle2) - Assert.AreNotEqual(seq, shuffle1) + Assert.AreNotEqual(intSeq, shuffle1) Assert.AreNotEqual(shuffle1, shuffle3) + [] + member _.RandomShuffleWithWrongArg() = + let nullSeq = null + let intSeq = seq { 1..20 } + let nullRand = null + let rand = Random(123) + + CheckThrowsArgumentNullException (fun () -> Seq.randomShuffleWith rand nullSeq |> ignore) + CheckThrowsArgumentNullException (fun () -> Seq.randomShuffleWith nullRand intSeq |> ignore) + [] member _.RandomChoice() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..5000 } - // try choice six times, if all are same, it must be broken + // try choice five times, if all are same, it must be broken let results = [| - Seq.randomChoice seq - Seq.randomChoice seq - Seq.randomChoice seq - Seq.randomChoice seq - Seq.randomChoice seq - Seq.randomChoice seq + Seq.randomChoice intSeq + Seq.randomChoice intSeq + Seq.randomChoice intSeq + Seq.randomChoice intSeq + Seq.randomChoice intSeq |] let allSame = results |> Array.forall (fun x -> x = results.[0]) Assert.False(allSame) - // empty seq + [] + member _.RandomChoiceWrongArg() = + let nullSeq = null let emptySeq = Seq.empty + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoice nullSeq |> ignore) CheckThrowsArgumentException (fun () -> Seq.randomChoice emptySeq |> ignore) [] member _.RandomChoiceWith() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..5000 } let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choice1 = seq |> Seq.randomChoiceWith rand1 - let choice2 = seq |> Seq.randomChoiceWith rand2 - let choice3 = seq |> Seq.randomChoiceWith rand3 + let choice1 = intSeq |> Seq.randomChoiceWith rand1 + let choice2 = intSeq |> Seq.randomChoiceWith rand2 + let choice3 = intSeq |> Seq.randomChoiceWith rand3 Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) + [] + member _.RandomChoiceWithWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..20 } + let nullRand = null + let rand = Random(123) + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoiceWith rand nullSeq |> ignore) + CheckThrowsArgumentNullException (fun () -> Seq.randomChoiceWith nullRand intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoiceWith rand emptySeq |> ignore) + [] member _.RandomChoices() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..50 } - // try choices three times, if all are same, it must be broken - let choicesLength = 5 - let results = [| - Seq.randomChoices choicesLength seq |> Seq.cache - Seq.randomChoices choicesLength seq |> Seq.cache - Seq.randomChoices choicesLength seq |> Seq.cache - |] - let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) - let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) - Assert.False(allSame) - Assert.True(allCorrectLength) + let choicesLength = 20 + let choice1 = intSeq |> Seq.randomChoices choicesLength |> Seq.cache + let choice2 = intSeq |> Seq.randomChoices choicesLength |> Seq.cache - // empty seq - let emptySeq = Seq.empty - CheckThrowsArgumentException (fun () -> Seq.randomChoices choicesLength emptySeq |> ignore) + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1 |> Seq.length) + Assert.AreEqual(choicesLength, choice2 |> Seq.length) + + let intSeq = seq { 1; 2 } + let choices = intSeq |> Seq.randomChoices choicesLength + Assert.AreEqual(choicesLength, choices |> Seq.length) + Assert.AreEqual(intSeq, choices |> Seq.distinct |> Seq.sort) - // negative choices length + [] + member _.RandomChoicesWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let choicesLength = 20 let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Seq.randomChoices negativeChoicesLength seq |> ignore) + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoices choicesLength nullSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoices choicesLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoices negativeChoicesLength intSeq |> ignore) [] member _.RandomChoicesWith() = - let seq = seq {1;2;3;4;5;6;7;8} + let seq = seq { 1..50 } let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 - let choice1 = seq |> Seq.randomChoicesWith rand1 choicesLength - let choice2 = seq |> Seq.randomChoicesWith rand2 choicesLength - let choice3 = seq |> Seq.randomChoicesWith rand3 choicesLength + let choicesLength = 20 + let choice1 = seq |> Seq.randomChoicesWith rand1 choicesLength |> Seq.cache + let choice2 = seq |> Seq.randomChoicesWith rand2 choicesLength |> Seq.cache + let choice3 = seq |> Seq.randomChoicesWith rand3 choicesLength |> Seq.cache Assert.AreEqual(choice1, choice2) Assert.AreNotEqual(choice1, choice3) + [] + member _.RandomChoicesWithWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let nullRand = null + let rand = Random(123) + let choicesLength = 20 + let negativeChoicesLength = -1 + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoicesWith rand choicesLength nullSeq |> ignore) + CheckThrowsArgumentNullException (fun () -> Seq.randomChoicesWith nullRand choicesLength intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoicesWith rand choicesLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoicesWith rand negativeChoicesLength intSeq |> ignore) + [] member _.RandomSample() = - let seq = Seq.init 50 id - let choicesLengthSmall = 1 - let choicesLengthLarge = 49 - - let verify choicesLength tryCount = - // try sample tryCount times, if all are same, it must be broken - let results = [| - for _ in 1..tryCount do - Seq.randomSample choicesLength seq |> Seq.cache - |] - let allSame = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x results.[0]) = 0) - let allCorrectLength = results |> Array.forall (fun x -> (Seq.length x) = choicesLength) - let allDistinct = results |> Array.forall (fun x -> (Seq.compareWith Operators.compare x (x |> Seq.distinct)) = 0) - Assert.False(allSame) - Assert.True(allCorrectLength) - Assert.True(allDistinct) - - verify choicesLengthSmall 10 - verify choicesLengthLarge 2 + let intSeq = seq { 1..50 } - // empty seq - let emptySeq = Seq.empty - CheckThrowsArgumentException (fun () -> Seq.randomSample choicesLengthSmall emptySeq |> ignore) + let choicesLength = 20 + let choice1 = intSeq |> Seq.randomSample choicesLength |> Seq.cache + let choice2 = intSeq |> Seq.randomSample choicesLength |> Seq.cache - // negative choices length - let negativeChoicesLength = -1 - CheckThrowsArgumentException (fun () -> Seq.randomSample negativeChoicesLength seq |> ignore) + Assert.AreNotEqual(choice1, choice2) + Assert.AreEqual(choicesLength, choice1 |> Seq.length) + Assert.AreEqual(choicesLength, choice2 |> Seq.length) + + Assert.AreEqual(choice1, choice1 |> Seq.distinct) + Assert.AreEqual(choice2, choice2 |> Seq.distinct) + + [] + member _.RandomSampleWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 - // invalid count - let invalidCountRange = 100 - CheckThrowsArgumentException (fun () -> Seq.randomSample invalidCountRange seq |> ignore) + CheckThrowsArgumentNullException (fun () -> Seq.randomSample sampleLength nullSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample sampleLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample negativeSampleLength intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSample tooBigSampleLength intSeq |> ignore) [] member _.RandomSampleWith() = - let seq = seq {1;2;3;4;5;6;7;8} + let intSeq = seq { 1..50 } let rand1 = Random(123) let rand2 = Random(123) let rand3 = Random(321) - let choicesLength = 5 - let choice1 = seq |> Seq.randomSampleWith rand1 choicesLength - let choice2 = seq |> Seq.randomSampleWith rand2 choicesLength - let choice3 = seq |> Seq.randomSampleWith rand3 choicesLength + let choicesLength = 20 + let choice1 = intSeq |> Seq.randomSampleWith rand1 choicesLength |> Seq.cache + let choice2 = intSeq |> Seq.randomSampleWith rand2 choicesLength |> Seq.cache + let choice3 = intSeq |> Seq.randomSampleWith rand3 choicesLength |> Seq.cache Assert.AreEqual(choice1, choice2) - Assert.AreNotEqual(choice1, choice3) \ No newline at end of file + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1 |> Seq.length) + Assert.AreEqual(choicesLength, choice3 |> Seq.length) + Assert.AreEqual(choice1, choice1 |> Seq.distinct) + Assert.AreEqual(choice3, choice3 |> Seq.distinct) + + [] + member _.RandomSampleWithWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let nullRand = null + let rand = Random(123) + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentNullException (fun () -> Seq.randomSampleWith rand sampleLength nullSeq |> ignore) + CheckThrowsArgumentNullException (fun () -> Seq.randomSampleWith nullRand sampleLength intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand sampleLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand negativeSampleLength intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand tooBigSampleLength intSeq |> ignore) \ No newline at end of file From dfc6692802ef95fc7e7cfaa88eaab249b56fa90e Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Tue, 4 Jun 2024 13:27:45 -0700 Subject: [PATCH 11/25] Random functions: addded *By tests to arrays and sequences --- src/FSharp.Core/array.fs | 2 +- .../ArrayModule.fs | 150 +++++++++++++++++- .../Microsoft.FSharp.Collections/SeqModule.fs | 120 +++++++++++++- 3 files changed, 269 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 65fe47a723a..31372fc6ebc 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2103,7 +2103,7 @@ module Array = if inputLength <= setSize then let pool = copy source for i = 0 to count - 1 do - let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - i + let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) result[i] <- pool[j] pool[j] <- pool[inputLength - i - 1] else diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 037279c5ae3..d6285ff6160 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -2003,6 +2003,32 @@ type ArrayModule() = CheckThrowsArgumentNullException (fun () -> Array.randomShuffleWith rand nullArr |> ignore) CheckThrowsArgumentNullException (fun () -> Array.randomShuffleWith nullRand arr |> ignore) + [] + member _.RandomShuffleBy() = + let arr = [| 1..20 |] + + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = arr |> Array.randomShuffleBy rand1.NextDouble + let shuffle2 = arr |> Array.randomShuffleBy rand2.NextDouble + let shuffle3 = arr |> Array.randomShuffleBy rand3.NextDouble + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(arr, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.RandomShuffleByWrongArg() = + let nullArr = null + let arr = [| 1..20 |] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleBy randomizer nullArr |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleBy wrongRandomizer arr |> ignore) + [] member _.RandomShuffleInPlace() = let arr = [| 1..20 |] @@ -2051,6 +2077,36 @@ type ArrayModule() = CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceWith rand nullArr |> ignore) CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceWith nullRand arr |> ignore) + [] + member _.RandomShuffleInPlaceBy() = + let arr = [| 1..20 |] + + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = [| 1..20 |] + let shuffle2 = [| 1..20 |] + let shuffle3 = [| 1..20 |] + + shuffle1 |> Array.randomShuffleInPlaceBy rand1.NextDouble + shuffle2 |> Array.randomShuffleInPlaceBy rand2.NextDouble + shuffle3 |> Array.randomShuffleInPlaceBy rand3.NextDouble + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(arr, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.RandomShuffleInPlaceByWrongArg() = + let nullArr = null + let arr = [| 1..20 |] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceBy randomizer nullArr |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleInPlaceBy wrongRandomizer arr |> ignore) + [] member _.RandomChoice() = let arr = [| 1..5000 |] @@ -2100,6 +2156,32 @@ type ArrayModule() = CheckThrowsArgumentNullException (fun () -> Array.randomChoiceWith nullRand arr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomChoiceWith rand emptyArr |> ignore) + [] + member _.RandomChoiceBy() = + let arr = [| 1..5000 |] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = arr |> Array.randomChoiceBy rand1.NextDouble + let choice2 = arr |> Array.randomChoiceBy rand2.NextDouble + let choice3 = arr |> Array.randomChoiceBy rand3.NextDouble + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoiceByWrongArg() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..20 |] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentNullException (fun () -> Array.randomChoiceBy randomizer nullArr |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoiceBy wrongRandomizer arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoiceBy randomizer emptyArr |> ignore) + [] member _.RandomChoices() = let arr = [| 1..50 |] @@ -2159,6 +2241,36 @@ type ArrayModule() = CheckThrowsArgumentException (fun () -> Array.randomChoicesWith rand choicesLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomChoicesWith rand negativeChoicesLength arr |> ignore) + [] + member _.RandomChoicesBy() = + let arr = [| 1..50 |] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = arr |> Array.randomChoicesBy rand1.NextDouble choicesLength + let choice2 = arr |> Array.randomChoicesBy rand2.NextDouble choicesLength + let choice3 = arr |> Array.randomChoicesBy rand3.NextDouble choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoicesByWrongArg() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..50 |] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let choicesLength = 20 + let negativeChoicesLength = -1 + + CheckThrowsArgumentNullException (fun () -> Array.randomChoicesBy randomizer choicesLength nullArr |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoicesBy wrongRandomizer choicesLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoicesBy randomizer choicesLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomChoicesBy randomizer negativeChoicesLength arr |> ignore) + [] member _.RandomSample() = let arr = [| 1..50 |] @@ -2221,4 +2333,40 @@ type ArrayModule() = CheckThrowsArgumentNullException (fun () -> Array.randomSampleWith nullRand sampleLength arr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand sampleLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand negativeSampleLength arr |> ignore) - CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand tooBigSampleLength arr |> ignore) \ No newline at end of file + CheckThrowsArgumentException (fun () -> Array.randomSampleWith rand tooBigSampleLength arr |> ignore) + + [] + member _.RandomSampleBy() = + let arr = [| 1..50 |] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = arr |> Array.randomSampleBy rand1.NextDouble choicesLength + let choice2 = arr |> Array.randomSampleBy rand2.NextDouble choicesLength + let choice3 = arr |> Array.randomSampleBy rand3.NextDouble choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice3.Length) + Assert.AreEqual(choice1, choice1 |> Array.distinct) + Assert.AreEqual(choice3, choice3 |> Array.distinct) + + [] + member _.RandomSampleByWrongArg() = + let nullArr = null + let emptyArr = [||] + let arr = [| 1..50 |] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentNullException (fun () -> Array.randomSampleBy randomizer sampleLength nullArr |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomSampleBy wrongRandomizer sampleLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer sampleLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer negativeSampleLength arr |> ignore) + CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer tooBigSampleLength arr |> ignore) \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index 8e89a6b451f..4a85886fa48 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1202,6 +1202,32 @@ type SeqModule() = CheckThrowsArgumentNullException (fun () -> Seq.randomShuffleWith rand nullSeq |> ignore) CheckThrowsArgumentNullException (fun () -> Seq.randomShuffleWith nullRand intSeq |> ignore) + [] + member _.RandomShuffleBy() = + let intSeq = seq { 1..20 } + + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = intSeq |> Seq.randomShuffleBy rand1.NextDouble |> Seq.cache + let shuffle2 = intSeq |> Seq.randomShuffleBy rand2.NextDouble |> Seq.cache + let shuffle3 = intSeq |> Seq.randomShuffleBy rand3.NextDouble |> Seq.cache + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(intSeq, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.RandomShuffleByWrongArg() = + let nullSeq = null + let intSeq = seq { 1..20 } + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentNullException (fun () -> Seq.randomShuffleBy randomizer nullSeq |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Seq.randomShuffleBy wrongRandomizer intSeq |> ignore) + [] member _.RandomChoice() = let intSeq = seq { 1..5000 } @@ -1251,6 +1277,32 @@ type SeqModule() = CheckThrowsArgumentNullException (fun () -> Seq.randomChoiceWith nullRand intSeq |> ignore) CheckThrowsArgumentException (fun () -> Seq.randomChoiceWith rand emptySeq |> ignore) + [] + member _.RandomChoiceBy() = + let intSeq = seq { 1..5000 } + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = intSeq |> Seq.randomChoiceBy rand1.NextDouble + let choice2 = intSeq |> Seq.randomChoiceBy rand2.NextDouble + let choice3 = intSeq |> Seq.randomChoiceBy rand3.NextDouble + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoiceByWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..20 } + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoiceBy randomizer nullSeq |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Seq.randomChoiceBy wrongRandomizer intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoiceBy randomizer emptySeq |> ignore) + [] member _.RandomChoices() = let intSeq = seq { 1..50 } @@ -1310,6 +1362,36 @@ type SeqModule() = CheckThrowsArgumentException (fun () -> Seq.randomChoicesWith rand choicesLength emptySeq |> ignore) CheckThrowsArgumentException (fun () -> Seq.randomChoicesWith rand negativeChoicesLength intSeq |> ignore) + [] + member _.RandomChoicesBy() = + let seq = seq { 1..50 } + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = seq |> Seq.randomChoicesBy rand1.NextDouble choicesLength |> Seq.cache + let choice2 = seq |> Seq.randomChoicesBy rand2.NextDouble choicesLength |> Seq.cache + let choice3 = seq |> Seq.randomChoicesBy rand3.NextDouble choicesLength |> Seq.cache + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoicesByWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let choicesLength = 20 + let negativeChoicesLength = -1 + + CheckThrowsArgumentNullException (fun () -> Seq.randomChoicesBy randomizer choicesLength nullSeq |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Seq.randomChoicesBy wrongRandomizer choicesLength intSeq |> Seq.toList |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoicesBy randomizer choicesLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomChoicesBy randomizer negativeChoicesLength intSeq |> ignore) + [] member _.RandomSample() = let intSeq = seq { 1..50 } @@ -1373,4 +1455,40 @@ type SeqModule() = CheckThrowsArgumentNullException (fun () -> Seq.randomSampleWith nullRand sampleLength intSeq |> ignore) CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand sampleLength emptySeq |> ignore) CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand negativeSampleLength intSeq |> ignore) - CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand tooBigSampleLength intSeq |> ignore) \ No newline at end of file + CheckThrowsArgumentException (fun () -> Seq.randomSampleWith rand tooBigSampleLength intSeq |> ignore) + + [] + member _.RandomSampleBy() = + let intSeq = seq { 1..50 } + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = intSeq |> Seq.randomSampleBy rand1.NextDouble choicesLength |> Seq.cache + let choice2 = intSeq |> Seq.randomSampleBy rand2.NextDouble choicesLength |> Seq.cache + let choice3 = intSeq |> Seq.randomSampleBy rand3.NextDouble choicesLength |> Seq.cache + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1 |> Seq.length) + Assert.AreEqual(choicesLength, choice3 |> Seq.length) + Assert.AreEqual(choice1, choice1 |> Seq.distinct) + Assert.AreEqual(choice3, choice3 |> Seq.distinct) + + [] + member _.RandomSampleByWrongArg() = + let nullSeq = null + let emptySeq = Seq.empty + let intSeq = seq { 1..50 } + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentNullException (fun () -> Seq.randomSampleBy randomizer sampleLength nullSeq |> ignore) + CheckThrowsArgumentOutOfRangeException (fun () -> Seq.randomSampleBy wrongRandomizer sampleLength intSeq |> Seq.toList |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleBy randomizer sampleLength emptySeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleBy randomizer negativeSampleLength intSeq |> ignore) + CheckThrowsArgumentException (fun () -> Seq.randomSampleBy randomizer tooBigSampleLength intSeq |> ignore) \ No newline at end of file From 957bcfa1b12cc4c23f2559c9167da1e00ddb7be6 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Tue, 4 Jun 2024 13:42:01 -0700 Subject: [PATCH 12/25] Random functions: addded *By tests to lists --- .../ListModule.fs | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs index 63840ba45c1..9f3ae062d02 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListModule.fs @@ -1121,6 +1121,29 @@ type ListModule() = CheckThrowsArgumentNullException (fun () -> List.randomShuffleWith nullRand list |> ignore) + [] + member _.RandomShuffleBy() = + let arr = [ 1..20 ] + + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let shuffle1 = arr |> List.randomShuffleBy rand1.NextDouble + let shuffle2 = arr |> List.randomShuffleBy rand2.NextDouble + let shuffle3 = arr |> List.randomShuffleBy rand3.NextDouble + + Assert.AreEqual(shuffle1, shuffle2) + Assert.AreNotEqual(arr, shuffle1) + Assert.AreNotEqual(shuffle1, shuffle3) + + [] + member _.RandomShuffleByWrongArg() = + let list = [ 1..20 ] + let wrongRandomizer = fun () -> 1.0 + + CheckThrowsArgumentOutOfRangeException (fun () -> List.randomShuffleBy wrongRandomizer list |> ignore) + [] member _.RandomChoice() = let list = [ 1..5000 ] @@ -1166,6 +1189,30 @@ type ListModule() = CheckThrowsArgumentNullException (fun () -> List.randomChoiceWith nullRand list |> ignore) CheckThrowsArgumentException (fun () -> List.randomChoiceWith rand emptyList |> ignore) + [] + member _.RandomChoiceBy() = + let list = [ 1..5000 ] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choice1 = list |> List.randomChoiceBy rand1.NextDouble + let choice2 = list |> List.randomChoiceBy rand2.NextDouble + let choice3 = list |> List.randomChoiceBy rand3.NextDouble + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoiceByWrongArg() = + let emptyList = [] + let list = [ 1..20 ] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + + CheckThrowsArgumentOutOfRangeException (fun () -> List.randomChoiceBy wrongRandomizer list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoiceBy randomizer emptyList |> ignore) + [] member _.RandomChoices() = let list = [ 1..50 ] @@ -1221,6 +1268,33 @@ type ListModule() = CheckThrowsArgumentException (fun () -> List.randomChoicesWith rand choicesLength emptyList |> ignore) CheckThrowsArgumentException (fun () -> List.randomChoicesWith rand negativeChoicesLength list |> ignore) + [] + member _.RandomChoicesBy() = + let list = [ 1..50 ] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = list |> List.randomChoicesBy rand1.NextDouble choicesLength + let choice2 = list |> List.randomChoicesBy rand2.NextDouble choicesLength + let choice3 = list |> List.randomChoicesBy rand3.NextDouble choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + + [] + member _.RandomChoicesByWrongArg() = + let emptyList = [] + let list = [ 1..50 ] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let choicesLength = 20 + let negativeChoicesLength = -1 + + CheckThrowsArgumentOutOfRangeException (fun () -> List.randomChoicesBy wrongRandomizer choicesLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoicesBy randomizer choicesLength emptyList |> ignore) + CheckThrowsArgumentException (fun () -> List.randomChoicesBy randomizer negativeChoicesLength list |> ignore) [] member _.RandomSample() = @@ -1280,4 +1354,38 @@ type ListModule() = CheckThrowsArgumentNullException (fun () -> List.randomSampleWith nullRand sampleLength list |> ignore) CheckThrowsArgumentException (fun () -> List.randomSampleWith rand sampleLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> List.randomSampleWith rand negativeSampleLength list |> ignore) - CheckThrowsArgumentException (fun () -> List.randomSampleWith rand tooBigSampleLength list |> ignore) \ No newline at end of file + CheckThrowsArgumentException (fun () -> List.randomSampleWith rand tooBigSampleLength list |> ignore) + + [] + member _.RandomSampleBy() = + let list = [ 1..50 ] + let rand1 = Random(123) + let rand2 = Random(123) + let rand3 = Random(321) + + let choicesLength = 20 + let choice1 = list |> List.randomSampleBy rand1.NextDouble choicesLength + let choice2 = list |> List.randomSampleBy rand2.NextDouble choicesLength + let choice3 = list |> List.randomSampleBy rand3.NextDouble choicesLength + + Assert.AreEqual(choice1, choice2) + Assert.AreNotEqual(choice1, choice3) + Assert.AreEqual(choicesLength, choice1.Length) + Assert.AreEqual(choicesLength, choice3.Length) + Assert.AreEqual(choice1, choice1 |> List.distinct) + Assert.AreEqual(choice3, choice3 |> List.distinct) + + [] + member _.RandomSampleByWrongArg() = + let emptyArr = [] + let list = [ 1..50 ] + let wrongRandomizer = fun () -> 1.0 + let randomizer = Random(123).NextDouble + let tooBigSampleLength = 100 + let negativeSampleLength = -1 + let sampleLength = 20 + + CheckThrowsArgumentOutOfRangeException (fun () -> List.randomSampleBy wrongRandomizer sampleLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleBy randomizer sampleLength emptyArr |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleBy randomizer negativeSampleLength list |> ignore) + CheckThrowsArgumentException (fun () -> List.randomSampleBy randomizer tooBigSampleLength list |> ignore) \ No newline at end of file From 4488f230396e70483cd6c335b6460e7674594364 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Tue, 4 Jun 2024 15:05:41 -0700 Subject: [PATCH 13/25] Random functions: try fix CI --- FSharp.sln | 1 + docs/release-notes/.FSharp.Core/8.0.400.md | 2 ++ src/FSharp.Core/Random.fs | 20 ++++++++------ src/FSharp.Core/Random.fsi | 2 +- src/FSharp.Core/array.fs | 31 +++++++++++++++++++--- src/FSharp.Core/array.fsi | 30 ++++++++++----------- src/FSharp.Core/list.fs | 29 +++++++++++++++++--- src/FSharp.Core/seq.fs | 29 +++++++++++++++++--- src/FSharp.Core/seq.fsi | 24 ++++++++--------- 9 files changed, 120 insertions(+), 48 deletions(-) diff --git a/FSharp.sln b/FSharp.sln index 2527bf8b5eb..71f0acefc0a 100644 --- a/FSharp.sln +++ b/FSharp.sln @@ -148,6 +148,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Core", ".FSharp.Cor ProjectSection(SolutionItems) = preProject docs\release-notes\.FSharp.Core\8.0.200.md = docs\release-notes\.FSharp.Core\8.0.200.md docs\release-notes\.FSharp.Core\8.0.300.md = docs\release-notes\.FSharp.Core\8.0.300.md + docs\release-notes\.FSharp.Core\8.0.400.md = docs\release-notes\.FSharp.Core\8.0.400.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Language", ".Language", "{1478B841-73BD-4E68-8F23-413ABB0B991F}" diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index ee24d8d0a53..f3ab78ba1e0 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -2,6 +2,8 @@ ### Added +* `Random functions for collections` ([RFC #1135](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1135-random-functions-for-collections.md), [PR #17277](https://github.com/dotnet/fsharp/pull/17277)) + ### Changed * Cache delegate in query extensions. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130)) diff --git a/src/FSharp.Core/Random.fs b/src/FSharp.Core/Random.fs index 9ca3e43a477..b69f889af78 100644 --- a/src/FSharp.Core/Random.fs +++ b/src/FSharp.Core/Random.fs @@ -7,13 +7,17 @@ open System.Threading [] type internal ThreadSafeRandom() = - [] static val mutable private globalRandom : Random - [] static val mutable private localRandom : ThreadLocal + [] + static val mutable private globalRandom: Random + + [] + static val mutable private localRandom: ThreadLocal + static do ThreadSafeRandom.globalRandom <- Random() - static do ThreadSafeRandom.localRandom <- new ThreadLocal(fun () -> - lock ThreadSafeRandom.globalRandom (fun () -> - Random(ThreadSafeRandom.globalRandom.Next()) - ) - ) + + static do + ThreadSafeRandom.localRandom <- + new ThreadLocal(fun () -> + lock ThreadSafeRandom.globalRandom (fun () -> Random(ThreadSafeRandom.globalRandom.Next()))) // Don't pass the returned Random object between threads - static member Shared = ThreadSafeRandom.localRandom.Value \ No newline at end of file + static member Shared = ThreadSafeRandom.localRandom.Value diff --git a/src/FSharp.Core/Random.fsi b/src/FSharp.Core/Random.fsi index ecb087320df..e65feb415a0 100644 --- a/src/FSharp.Core/Random.fsi +++ b/src/FSharp.Core/Random.fsi @@ -4,4 +4,4 @@ open System [] type internal ThreadSafeRandom = - static member Shared: Random \ No newline at end of file + static member Shared: Random diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 31372fc6ebc..3f64ae4cc37 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1984,6 +1984,7 @@ module Array = checkNonNull "random" random let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -1995,6 +1996,7 @@ module Array = checkNonNull "source" source let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -2014,6 +2016,7 @@ module Array = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -2022,6 +2025,7 @@ module Array = for i = 0 to count - 1 do let j = random.Next(0, inputLength) result[i] <- source[j] + result [] @@ -2032,6 +2036,7 @@ module Array = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -2040,6 +2045,7 @@ module Array = for i = 0 to count - 1 do let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength result[i] <- source[j] + result [] @@ -2055,6 +2061,7 @@ module Array = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -2064,19 +2071,25 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = copy source + for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) result[i] <- pool[j] pool[j] <- pool[inputLength - i - 1] else let selected = HashSet() + for i = 0 to count - 1 do let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do j <- random.Next(0, inputLength) + selected.Add(j) |> ignore result[i] <- source[j] @@ -2090,6 +2103,7 @@ module Array = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString @@ -2099,19 +2113,28 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = copy source + for i = 0 to count - 1 do - let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + let j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + result[i] <- pool[j] pool[j] <- pool[inputLength - i - 1] else let selected = HashSet() + for i = 0 to count - 1 do - let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + let mutable j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore result[i] <- source[j] diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index adf742ca104..8880bc59218 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3112,7 +3112,7 @@ module Array = /// Can evaluate to [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffle : source: 'T array -> 'T array + val randomShuffle: source: 'T array -> 'T array /// Return a new array shuffled in a random order with the specified Random instance. /// @@ -3133,7 +3133,7 @@ module Array = /// Can evaluate to [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleWith : random: Random -> source: 'T array -> 'T array + val randomShuffleWith: random: Random -> source: 'T array -> 'T array /// Return a new array shuffled in a random order using the specified randomizer function. /// @@ -3154,7 +3154,7 @@ module Array = /// Can evaluate to [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleBy : randomizer: (unit -> float) -> source: 'T array -> 'T array + val randomShuffleBy: randomizer: (unit -> float) -> source: 'T array -> 'T array /// Sorts input array in a random order by mutating the array in-place. /// @@ -3171,7 +3171,7 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlace : source: 'T array -> unit + val randomShuffleInPlace: source: 'T array -> unit /// Sorts input array in a random order with the specified Random instance by mutating the array in-place. /// @@ -3189,7 +3189,7 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlaceWith : random: Random -> source: 'T array -> unit + val randomShuffleInPlaceWith: random: Random -> source: 'T array -> unit /// Sorts input array in a random order using the specified randomizer function by mutating the array in-place. /// @@ -3208,7 +3208,7 @@ module Array = /// After evaluation array can contain [| 0; 2; 4; 3; 1 |]. /// [] - val randomShuffleInPlaceBy : randomizer: (unit -> float) -> source: 'T array -> unit + val randomShuffleInPlaceBy: randomizer: (unit -> float) -> source: 'T array -> unit /// Returns a random element from the given array. /// @@ -3228,7 +3228,7 @@ module Array = /// Can evaluate to 3. /// [] - val randomChoice : source: 'T array -> 'T + val randomChoice: source: 'T array -> 'T /// Returns a random element from the given array with the specified Random instance. /// @@ -3250,7 +3250,7 @@ module Array = /// Can evaluate to 3. /// [] - val randomChoiceWith : random: Random -> source: 'T array -> 'T + val randomChoiceWith: random: Random -> source: 'T array -> 'T /// Returns a random element from the given array using the specified randomizer function. /// @@ -3272,7 +3272,7 @@ module Array = /// Can evaluate to 3. /// [] - val randomChoiceBy : randomizer: (unit -> float) -> source: 'T array -> 'T + val randomChoiceBy: randomizer: (unit -> float) -> source: 'T array -> 'T /// Returns an array of random elements from the given array, each element can be selected multiple times. /// @@ -3294,7 +3294,7 @@ module Array = /// Can evaluate to [| 3; 1; 3 |]. /// [] - val randomChoices : count: int -> source: 'T array -> 'T array + val randomChoices: count: int -> source: 'T array -> 'T array /// Returns an array of random elements from the given array with the specified Random instance, each element can be selected multiple times. /// @@ -3318,7 +3318,7 @@ module Array = /// Can evaluate to [| 3; 1; 3 |]. /// [] - val randomChoicesWith : random: Random -> count: int -> source: 'T array -> 'T array + val randomChoicesWith: random: Random -> count: int -> source: 'T array -> 'T array /// Returns an array of random elements from the given array using the specified randomizer function, each element can be selected multiple times. /// @@ -3342,7 +3342,7 @@ module Array = /// Can evaluate to [| 3; 1; 3 |]. /// [] - val randomChoicesBy : randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array + val randomChoicesBy: randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array /// Returns a random sample of elements from the given array, each element can be selected only once. /// @@ -3365,7 +3365,7 @@ module Array = /// Can evaluate to [| 3; 1; 2 |]. /// [] - val randomSample : count: int -> source: 'T array -> 'T array + val randomSample: count: int -> source: 'T array -> 'T array /// Returns a random sample of elements from the given array with the specified Random instance, each element can be selected only once. /// @@ -3390,7 +3390,7 @@ module Array = /// Can evaluate to [| 3; 1; 2 |]. /// [] - val randomSampleWith : random: Random -> count: int -> source: 'T array -> 'T array + val randomSampleWith: random: Random -> count: int -> source: 'T array -> 'T array /// Returns a random sample of elements from the given array using the specified randomizer function, each element can be selected only once. /// @@ -3415,7 +3415,7 @@ module Array = /// Can evaluate to [| 3; 1; 2 |]. /// [] - val randomSampleBy : randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array + val randomSampleBy: randomizer: (unit -> float) -> count: int -> source: 'T array -> 'T array /// Provides parallel operations on arrays module Parallel = diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 027fa731521..1ac72933579 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1012,6 +1012,7 @@ module List = checkNonNull "random" random let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1021,6 +1022,7 @@ module List = [] let randomChoiceBy (randomizer: unit -> float) (source: 'T list) : 'T = let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1039,6 +1041,7 @@ module List = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1054,6 +1057,7 @@ module List = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1075,6 +1079,7 @@ module List = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1082,9 +1087,12 @@ module List = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = source |> toArray + [ for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) @@ -1093,11 +1101,14 @@ module List = ] else let selected = HashSet() + [ for _ = 0 to count - 1 do let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do j <- random.Next(0, inputLength) + selected.Add(j) |> ignore source[j] ] @@ -1108,6 +1119,7 @@ module List = invalidArgInputMustBeNonNegative "count" count let inputLength = source.Length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1115,22 +1127,31 @@ module List = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = source |> toArray + [ for i = 0 to count - 1 do - let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + let j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + yield pool[j] pool[j] <- pool[inputLength - i - 1] ] else let selected = HashSet() + [ for _ = 0 to count - 1 do - let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + let mutable j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore source[j] ] diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 1912cfe9573..4c6fc8153a6 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1966,6 +1966,7 @@ module Seq = checkNonNull "random" random let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1977,6 +1978,7 @@ module Seq = checkNonNull "source" source let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -1996,6 +1998,7 @@ module Seq = invalidArgInputMustBeNonNegative "count" count let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2013,6 +2016,7 @@ module Seq = invalidArgInputMustBeNonNegative "count" count let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2035,6 +2039,7 @@ module Seq = invalidArgInputMustBeNonNegative "count" count let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2042,9 +2047,12 @@ module Seq = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = source |> toArray + seq { for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) @@ -2053,11 +2061,14 @@ module Seq = } else let selected = HashSet() + seq { for _ = 0 to count - 1 do let mutable j = random.Next(0, inputLength) + while selected.Contains(j) do j <- random.Next(0, inputLength) + selected.Add(j) |> ignore source |> item j } @@ -2070,6 +2081,7 @@ module Seq = invalidArgInputMustBeNonNegative "count" count let inputLength = source |> length + if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2077,22 +2089,31 @@ module Seq = invalidArgOutOfRange "count" count "source.Length" inputLength // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py - let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + let setSize = + Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count + if inputLength <= setSize then let pool = source |> toArray + seq { for i = 0 to count - 1 do - let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + let j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) + yield pool[j] pool[j] <- pool[inputLength - i - 1] } else let selected = HashSet() + seq { for _ = 0 to count - 1 do - let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + let mutable j = + Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + while selected.Contains(j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength + selected.Add(j) |> ignore source |> item j } diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 94b51c0483b..d190d74d24a 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2953,7 +2953,7 @@ module Seq = /// Can evaluate to seq { 0; 2; 4; 3; 1 }. /// [] - val randomShuffle : source: seq<'T> -> seq<'T> + val randomShuffle: source: seq<'T> -> seq<'T> /// Return a new sequence shuffled in a random order with the specified Random instance. /// @@ -2974,7 +2974,7 @@ module Seq = /// Can evaluate to seq { 0; 2; 4; 3; 1 }. /// [] - val randomShuffleWith : random: Random -> source: seq<'T> -> seq<'T> + val randomShuffleWith: random: Random -> source: seq<'T> -> seq<'T> /// Return a new sequence shuffled in a random order with the specified randomizer function. /// @@ -2994,7 +2994,7 @@ module Seq = /// Can evaluate to seq { 0; 2; 4; 3; 1 }. /// [] - val randomShuffleBy : randomizer: (unit -> float) -> source: seq<'T> -> seq<'T> + val randomShuffleBy: randomizer: (unit -> float) -> source: seq<'T> -> seq<'T> /// /// Returns a random element from the given sequence. @@ -3016,7 +3016,7 @@ module Seq = /// Can evaluate to 3. /// [] - val randomChoice : source: seq<'T> -> 'T + val randomChoice: source: seq<'T> -> 'T /// /// Returns a random element from the given sequence with the specified Random instance. @@ -3040,7 +3040,7 @@ module Seq = /// Can evaluate to 3. /// [] - val randomChoiceWith : random: Random -> source: seq<'T> -> 'T + val randomChoiceWith: random: Random -> source: seq<'T> -> 'T /// /// Returns a random element from the given sequence with the specified randomizer function. @@ -3065,7 +3065,7 @@ module Seq = /// Can evaluate to 3. /// [] - val randomChoiceBy : randomizer: (unit -> float) -> source: seq<'T> -> 'T + val randomChoiceBy: randomizer: (unit -> float) -> source: seq<'T> -> 'T /// /// Returns an sequence of random elements from the given sequence, each element can be selected multiple times. @@ -3089,7 +3089,7 @@ module Seq = /// Can evaluate to seq { 3; 1; 3 }. /// [] - val randomChoices : count: int -> source: seq<'T> -> seq<'T> + val randomChoices: count: int -> source: seq<'T> -> seq<'T> /// /// Returns a sequence of random elements from the given sequence with the specified Random instance, each element can be selected multiple times. @@ -3115,7 +3115,7 @@ module Seq = /// Can evaluate to seq { 3; 1; 3 }. /// [] - val randomChoicesWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> + val randomChoicesWith: random: Random -> count: int -> source: seq<'T> -> seq<'T> /// /// Returns a sequence of random elements from the given sequence with the specified randomizer function, each element can be selected multiple times. @@ -3141,7 +3141,7 @@ module Seq = /// Can evaluate to seq { 3; 1; 3 }. /// [] - val randomChoicesBy : randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> + val randomChoicesBy: randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> /// /// Returns a random sample of elements from the given sequence, each element can be selected only once. @@ -3166,7 +3166,7 @@ module Seq = /// Can evaluate to seq { 3; 1; 2 }. /// [] - val randomSample : count: int -> source: seq<'T> -> seq<'T> + val randomSample: count: int -> source: seq<'T> -> seq<'T> /// /// Returns a random sample of elements from the given sequence with the specified Random instance, each element can be selected only once. @@ -3193,7 +3193,7 @@ module Seq = /// Can evaluate to seq { 3; 1; 2 }. /// [] - val randomSampleWith : random: Random -> count: int -> source: seq<'T> -> seq<'T> + val randomSampleWith: random: Random -> count: int -> source: seq<'T> -> seq<'T> /// /// Returns a random sample of elements from the given sequence with the specified randomizer function, each element can be selected only once. @@ -3220,4 +3220,4 @@ module Seq = /// Can evaluate to seq { 3; 1; 2 }. /// [] - val randomSampleBy : randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> \ No newline at end of file + val randomSampleBy: randomizer: (unit -> float) -> count: int -> source: seq<'T> -> seq<'T> From a8b9a7da48f71490744fa5638826643bf6281c6b Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Tue, 4 Jun 2024 20:24:19 -0700 Subject: [PATCH 14/25] Random functions: review fixes --- FSharp.sln | 1 - src/FSharp.Core/array.fs | 20 +++++++++----------- src/FSharp.Core/list.fs | 16 ++++++++-------- src/FSharp.Core/local.fs | 7 +++---- src/FSharp.Core/seq.fs | 24 ++++++++++++------------ 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/FSharp.sln b/FSharp.sln index 71f0acefc0a..2527bf8b5eb 100644 --- a/FSharp.sln +++ b/FSharp.sln @@ -148,7 +148,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Core", ".FSharp.Cor ProjectSection(SolutionItems) = preProject docs\release-notes\.FSharp.Core\8.0.200.md = docs\release-notes\.FSharp.Core\8.0.200.md docs\release-notes\.FSharp.Core\8.0.300.md = docs\release-notes\.FSharp.Core\8.0.300.md - docs\release-notes\.FSharp.Core\8.0.400.md = docs\release-notes\.FSharp.Core\8.0.400.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Language", ".Language", "{1478B841-73BD-4E68-8F23-413ABB0B991F}" diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 3f64ae4cc37..a18075d1b3e 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1938,8 +1938,8 @@ module Array = [] let randomShuffleWith (random: Random) (source: 'T array) : 'T array = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source let result = copy source @@ -1963,8 +1963,8 @@ module Array = [] let randomShuffleInPlaceWith (random: Random) (source: 'T array) = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random source @@ -1980,8 +1980,8 @@ module Array = [] let randomChoiceWith (random: Random) (source: 'T array) : 'T = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source let inputLength = source.Length @@ -2009,8 +2009,8 @@ module Array = [] let randomChoicesWith (random: Random) (count: int) (source: 'T array) : 'T array = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2054,8 +2054,8 @@ module Array = [] let randomSampleWith (random: Random) (count: int) (source: 'T array) : 'T array = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2070,7 +2070,7 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -2087,10 +2087,9 @@ module Array = for i = 0 to count - 1 do let mutable j = random.Next(0, inputLength) - while selected.Contains(j) do + while not (selected.Add j) do j <- random.Next(0, inputLength) - selected.Add(j) |> ignore result[i] <- source[j] result @@ -2112,7 +2111,7 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -2132,10 +2131,9 @@ module Array = let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - while selected.Contains(j) do + while not (selected.Add j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - selected.Add(j) |> ignore result[i] <- source[j] result diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 1ac72933579..143e772d0c0 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1086,7 +1086,7 @@ module List = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -1096,8 +1096,9 @@ module List = [ for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) - yield pool[j] + let item = pool[j] pool[j] <- pool[inputLength - i - 1] + item ] else let selected = HashSet() @@ -1106,10 +1107,9 @@ module List = for _ = 0 to count - 1 do let mutable j = random.Next(0, inputLength) - while selected.Contains(j) do + while not (selected.Add j) do j <- random.Next(0, inputLength) - selected.Add(j) |> ignore source[j] ] @@ -1126,7 +1126,7 @@ module List = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -1138,8 +1138,9 @@ module List = let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) - yield pool[j] + let item = pool[j] pool[j] <- pool[inputLength - i - 1] + item ] else let selected = HashSet() @@ -1149,10 +1150,9 @@ module List = let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - while selected.Contains(j) do + while not (selected.Add j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - selected.Add(j) |> ignore source[j] ] diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index c61e4961394..275d13921a0 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -9,14 +9,13 @@ module internal DetailedExceptions = open System open Microsoft.FSharp.Core - /// takes an argument, a formatting string, a param array to splice into the formatting string let inline invalidArgFmt (arg:string) (format:string) paramArray = let msg = String.Format (format, paramArray) raise (new ArgumentException (msg, arg)) /// takes an argument, a formatting string, a param array to splice into the formatting string - let inline invalidArgOufOfRangeFmt (arg:string) (format:string) paramArray = + let inline invalidArgOutOfRangeFmt (arg:string) (format:string) paramArray = let msg = String.Format (format, paramArray) raise (new ArgumentOutOfRangeException (arg, msg)) @@ -1210,7 +1209,7 @@ module internal Random = let value = randomizer() if value < 0.0 || value >= 1.0 then let argName = nameof randomizer - invalidArgOufOfRangeFmt argName + invalidArgOutOfRangeFmt argName "{0}\n{1} returned {2}, should be in range [0.0, 1.0)." [|SR.GetString SR.outOfRange; argName; value|] value @@ -1243,5 +1242,5 @@ module internal Random = let getMaxSetSizeForSampling count = let mutable setSize = 21 if count > 5 then - setSize <- setSize + (4.0 ** Math.Ceiling(Math.Log(count * 3 |> float, 4)) |> int) + setSize <- setSize + (4.0 ** ceil (Math.Log(count * 3 |> float, 4)) |> int) setSize \ No newline at end of file diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 4c6fc8153a6..86f28b0572d 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1941,8 +1941,8 @@ module Seq = [] let randomShuffleWith (random: Random) (source: seq<'T>) : seq<'T> = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source let tempArray = toArray source Microsoft.FSharp.Primitives.Basics.Random.shuffleArrayInPlaceWith random tempArray @@ -1962,8 +1962,8 @@ module Seq = [] let randomChoiceWith (random: Random) (source: seq<'T>) : 'T = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source let inputLength = source |> length @@ -1991,8 +1991,8 @@ module Seq = [] let randomChoicesWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2032,8 +2032,8 @@ module Seq = [] let randomSampleWith (random: Random) (count: int) (source: seq<'T>) : seq<'T> = - checkNonNull "source" source checkNonNull "random" random + checkNonNull "source" source if count < 0 then invalidArgInputMustBeNonNegative "count" count @@ -2046,7 +2046,7 @@ module Seq = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -2056,8 +2056,9 @@ module Seq = seq { for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) - yield pool[j] + let item = pool[j] pool[j] <- pool[inputLength - i - 1] + item } else let selected = HashSet() @@ -2066,10 +2067,9 @@ module Seq = for _ = 0 to count - 1 do let mutable j = random.Next(0, inputLength) - while selected.Contains(j) do + while not (selected.Add j) do j <- random.Next(0, inputLength) - selected.Add(j) |> ignore source |> item j } @@ -2088,7 +2088,7 @@ module Seq = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/main/Lib/random.py + // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count @@ -2100,8 +2100,9 @@ module Seq = let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) - yield pool[j] + let item = pool[j] pool[j] <- pool[inputLength - i - 1] + item } else let selected = HashSet() @@ -2111,10 +2112,9 @@ module Seq = let mutable j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - while selected.Contains(j) do + while not (selected.Add j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - selected.Add(j) |> ignore source |> item j } From 5b4dbe1c4a81c066e0789a460a2811f1487e372b Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 5 Jun 2024 11:28:19 -0700 Subject: [PATCH 15/25] Try fix CI --- ...Core.SurfaceArea.netstandard20.release.bsl | 39 +++++++++++++++++++ ...Core.SurfaceArea.netstandard21.release.bsl | 39 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl index bac32123722..e1864618be6 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl @@ -126,6 +126,9 @@ Microsoft.FSharp.Collections.ArrayModule: T MaxBy[T,TResult](Microsoft.FSharp.Co Microsoft.FSharp.Collections.ArrayModule: T Max[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[]) Microsoft.FSharp.Collections.ArrayModule: T Min[T](T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoiceWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoice[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) Microsoft.FSharp.Collections.ArrayModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) Microsoft.FSharp.Collections.ArrayModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) @@ -164,6 +167,15 @@ Microsoft.FSharp.Collections.ArrayModule: T[] InsertManyAt[T](Int32, System.Coll Microsoft.FSharp.Collections.ArrayModule: T[] OfList[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ArrayModule: T[] OfSeq[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.ArrayModule: T[] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoicesWith[T](System.Random, Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoices[T](Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSampleWith[T](System.Random, Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSample[T](Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffleWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffle[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T[] RemoveAt[T](Int32, T[]) Microsoft.FSharp.Collections.ArrayModule: T[] RemoveManyAt[T](Int32, Int32, T[]) Microsoft.FSharp.Collections.ArrayModule: T[] Replicate[T](Int32, T) @@ -194,6 +206,9 @@ Microsoft.FSharp.Collections.ArrayModule: Void Iterate2[T1,T2](Microsoft.FSharp. Microsoft.FSharp.Collections.ArrayModule: Void IterateIndexed2[T1,T2](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T1,Microsoft.FSharp.Core.FSharpFunc`2[T2,Microsoft.FSharp.Core.Unit]]], T1[], T2[]) Microsoft.FSharp.Collections.ArrayModule: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[]) Microsoft.FSharp.Collections.ArrayModule: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlaceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlaceWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlace[T](T[]) Microsoft.FSharp.Collections.ArrayModule: Void Set[T](T[], Int32, T) Microsoft.FSharp.Collections.ArrayModule: Void SortInPlaceBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[]) Microsoft.FSharp.Collections.ArrayModule: Void SortInPlaceWith[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Int32]], T[]) @@ -329,6 +344,15 @@ Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] OfArray[T](T[]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] OfSeq[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoicesWith[T](System.Random, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoices[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSampleWith[T](System.Random, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSample[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffleWith[T](System.Random, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffle[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RemoveAt[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RemoveManyAt[T](Int32, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] Replicate[T](Int32, T) @@ -377,6 +401,9 @@ Microsoft.FSharp.Collections.ListModule: T MaxBy[T,TResult](Microsoft.FSharp.Cor Microsoft.FSharp.Collections.ListModule: T Max[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Min[T](Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoiceWith[T](System.Random, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoice[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) @@ -483,6 +510,15 @@ Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1 Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] OfArray[T](T[]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] OfList[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoicesWith[T](System.Random, Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoices[T](Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSampleWith[T](System.Random, Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSample[T](Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffleWith[T](System.Random, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffle[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] ReadOnly[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RemoveAt[T](Int32, System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RemoveManyAt[T](Int32, Int32, System.Collections.Generic.IEnumerable`1[T]) @@ -518,6 +554,9 @@ Microsoft.FSharp.Collections.SeqModule: T MaxBy[T,TResult](Microsoft.FSharp.Core Microsoft.FSharp.Collections.SeqModule: T Max[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Min[T](System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoiceWith[T](System.Random, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoice[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index 2d02c4cb1bb..92d85ccd98d 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -126,6 +126,9 @@ Microsoft.FSharp.Collections.ArrayModule: T MaxBy[T,TResult](Microsoft.FSharp.Co Microsoft.FSharp.Collections.ArrayModule: T Max[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[]) Microsoft.FSharp.Collections.ArrayModule: T Min[T](T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoiceWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: T RandomChoice[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) Microsoft.FSharp.Collections.ArrayModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) Microsoft.FSharp.Collections.ArrayModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], T[]) @@ -164,6 +167,15 @@ Microsoft.FSharp.Collections.ArrayModule: T[] InsertManyAt[T](Int32, System.Coll Microsoft.FSharp.Collections.ArrayModule: T[] OfList[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ArrayModule: T[] OfSeq[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.ArrayModule: T[] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoicesWith[T](System.Random, Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomChoices[T](Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSampleWith[T](System.Random, Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomSample[T](Int32, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffleWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: T[] RandomShuffle[T](T[]) Microsoft.FSharp.Collections.ArrayModule: T[] RemoveAt[T](Int32, T[]) Microsoft.FSharp.Collections.ArrayModule: T[] RemoveManyAt[T](Int32, Int32, T[]) Microsoft.FSharp.Collections.ArrayModule: T[] Replicate[T](Int32, T) @@ -194,6 +206,9 @@ Microsoft.FSharp.Collections.ArrayModule: Void Iterate2[T1,T2](Microsoft.FSharp. Microsoft.FSharp.Collections.ArrayModule: Void IterateIndexed2[T1,T2](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T1,Microsoft.FSharp.Core.FSharpFunc`2[T2,Microsoft.FSharp.Core.Unit]]], T1[], T2[]) Microsoft.FSharp.Collections.ArrayModule: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[]) Microsoft.FSharp.Collections.ArrayModule: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlaceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlaceWith[T](System.Random, T[]) +Microsoft.FSharp.Collections.ArrayModule: Void RandomShuffleInPlace[T](T[]) Microsoft.FSharp.Collections.ArrayModule: Void Set[T](T[], Int32, T) Microsoft.FSharp.Collections.ArrayModule: Void SortInPlaceBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[]) Microsoft.FSharp.Collections.ArrayModule: Void SortInPlaceWith[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Int32]], T[]) @@ -329,6 +344,15 @@ Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] OfArray[T](T[]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] OfSeq[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoicesWith[T](System.Random, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomChoices[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSampleWith[T](System.Random, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomSample[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffleWith[T](System.Random, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RandomShuffle[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RemoveAt[T](Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] RemoveManyAt[T](Int32, Int32, Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: Microsoft.FSharp.Collections.FSharpList`1[T] Replicate[T](Int32, T) @@ -377,6 +401,9 @@ Microsoft.FSharp.Collections.ListModule: T MaxBy[T,TResult](Microsoft.FSharp.Cor Microsoft.FSharp.Collections.ListModule: T Max[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Min[T](Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoiceWith[T](System.Random, Microsoft.FSharp.Collections.FSharpList`1[T]) +Microsoft.FSharp.Collections.ListModule: T RandomChoice[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.ListModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], Microsoft.FSharp.Collections.FSharpList`1[T]) @@ -483,6 +510,15 @@ Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1 Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] OfArray[T](T[]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] OfList[T](Microsoft.FSharp.Collections.FSharpList`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] Permute[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoicesBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoicesWith[T](System.Random, Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomChoices[T](Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSampleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSampleWith[T](System.Random, Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomSample[T](Int32, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffleBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffleWith[T](System.Random, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RandomShuffle[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] ReadOnly[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RemoveAt[T](Int32, System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: System.Collections.Generic.IEnumerable`1[T] RemoveManyAt[T](Int32, Int32, System.Collections.Generic.IEnumerable`1[T]) @@ -518,6 +554,9 @@ Microsoft.FSharp.Collections.SeqModule: T MaxBy[T,TResult](Microsoft.FSharp.Core Microsoft.FSharp.Collections.SeqModule: T Max[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T MinBy[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Min[T](System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoiceBy[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Double], System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoiceWith[T](System.Random, System.Collections.Generic.IEnumerable`1[T]) +Microsoft.FSharp.Collections.SeqModule: T RandomChoice[T](System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T ReduceBack[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Reduce[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Collections.SeqModule: T Sum$W[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]], System.Collections.Generic.IEnumerable`1[T]) From 82860d811246d0c3f531e41532c1f8fdad17d3c9 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Fri, 7 Jun 2024 20:26:14 -0700 Subject: [PATCH 16/25] Changed thread local implementation to thread static for performance per review --- src/FSharp.Core/Random.fs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/FSharp.Core/Random.fs b/src/FSharp.Core/Random.fs index b69f889af78..72c0c96c49b 100644 --- a/src/FSharp.Core/Random.fs +++ b/src/FSharp.Core/Random.fs @@ -3,21 +3,23 @@ namespace Microsoft.FSharp.Core open System +open System.Runtime.CompilerServices open System.Threading [] type internal ThreadSafeRandom() = - [] - static val mutable private globalRandom: Random [] - static val mutable private localRandom: ThreadLocal + [] + static val mutable private random: Random - static do ThreadSafeRandom.globalRandom <- Random() + [] + static member private Create() = + ThreadSafeRandom.random <- Random() + ThreadSafeRandom.random - static do - ThreadSafeRandom.localRandom <- - new ThreadLocal(fun () -> - lock ThreadSafeRandom.globalRandom (fun () -> Random(ThreadSafeRandom.globalRandom.Next()))) // Don't pass the returned Random object between threads - static member Shared = ThreadSafeRandom.localRandom.Value + static member Shared = + match ThreadSafeRandom.random with + | null -> ThreadSafeRandom.Create() + | random -> random From 38267432851a45d0c40eaa668cbb06ca969e589c Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Sat, 8 Jun 2024 14:59:03 -0700 Subject: [PATCH 17/25] PR review fix --- src/FSharp.Core/array.fs | 2 +- src/FSharp.Core/list.fs | 2 +- src/FSharp.Core/seq.fs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index a18075d1b3e..34c89a6cec9 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2082,7 +2082,7 @@ module Array = result[i] <- pool[j] pool[j] <- pool[inputLength - i - 1] else - let selected = HashSet() + let selected = HashSet(count) for i = 0 to count - 1 do let mutable j = random.Next(0, inputLength) diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 143e772d0c0..5ad7a8fed5e 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1143,7 +1143,7 @@ module List = item ] else - let selected = HashSet() + let selected = HashSet(count) [ for _ = 0 to count - 1 do diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 86f28b0572d..fa0b12c3d05 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2105,7 +2105,7 @@ module Seq = item } else - let selected = HashSet() + let selected = HashSet(count) seq { for _ = 0 to count - 1 do From 887a32f0aef4533d893d20c13f6de004ed186882 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Mon, 10 Jun 2024 21:43:16 -0700 Subject: [PATCH 18/25] PR fix --- src/FSharp.Core/seq.fs | 43 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index fa0b12c3d05..801f5d7b53f 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -1965,25 +1965,27 @@ module Seq = checkNonNull "random" random checkNonNull "source" source - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString let i = random.Next(0, inputLength) - source |> item i + tempArray[i] [] let randomChoiceBy (randomizer: unit -> float) (source: seq<'T>) : 'T = checkNonNull "source" source - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString let i = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - source |> item i + tempArray[i] [] let randomChoice (source: seq<'T>) : 'T = @@ -1997,7 +1999,8 @@ module Seq = if count < 0 then invalidArgInputMustBeNonNegative "count" count - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2005,7 +2008,7 @@ module Seq = seq { for _ = 0 to count - 1 do let j = random.Next(0, inputLength) - source |> item j + tempArray[j] } [] @@ -2015,7 +2018,8 @@ module Seq = if count < 0 then invalidArgInputMustBeNonNegative "count" count - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2023,7 +2027,7 @@ module Seq = seq { for _ = 0 to count - 1 do let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - source |> item j + tempArray[j] } [] @@ -2038,7 +2042,8 @@ module Seq = if count < 0 then invalidArgInputMustBeNonNegative "count" count - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2051,13 +2056,11 @@ module Seq = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count if inputLength <= setSize then - let pool = source |> toArray - seq { for i = 0 to count - 1 do let j = random.Next(0, inputLength - i) - let item = pool[j] - pool[j] <- pool[inputLength - i - 1] + let item = tempArray[j] + tempArray[j] <- tempArray[inputLength - i - 1] item } else @@ -2070,7 +2073,7 @@ module Seq = while not (selected.Add j) do j <- random.Next(0, inputLength) - source |> item j + tempArray[j] } [] @@ -2080,7 +2083,8 @@ module Seq = if count < 0 then invalidArgInputMustBeNonNegative "count" count - let inputLength = source |> length + let tempArray = toArray source + let inputLength = tempArray.Length if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString @@ -2093,20 +2097,17 @@ module Seq = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count if inputLength <= setSize then - let pool = source |> toArray - seq { for i = 0 to count - 1 do let j = Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 (inputLength - i) - let item = pool[j] - pool[j] <- pool[inputLength - i - 1] + let item = tempArray[j] + tempArray[j] <- tempArray[inputLength - i - 1] item } else let selected = HashSet(count) - seq { for _ = 0 to count - 1 do let mutable j = @@ -2115,7 +2116,7 @@ module Seq = while not (selected.Add j) do j <- Microsoft.FSharp.Primitives.Basics.Random.next randomizer 0 inputLength - source |> item j + tempArray[j] } [] From a9001fe5f7a884cf3e5f28aa7432a6253e72703b Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Tue, 11 Jun 2024 15:41:34 -0700 Subject: [PATCH 19/25] PR review fix --- src/FSharp.Core/local.fs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index 275d13921a0..52c06ea6ebc 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1215,11 +1215,7 @@ module internal Random = value let next (randomizer: unit -> float) (minValue: int) (maxValue: int) = - maxValue - minValue - |> float - |> (*) (executeRandomizer randomizer) - |> int - |> (+) minValue + int ((executeRandomizer randomizer) * float (maxValue - minValue)) + minValue let shuffleArrayInPlaceWith (random: Random) (array: array<'T>) = let inputLength = array.Length From c5943be9eb92cea6aadaa47cb3de6e33d7b0f44a Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 12 Jun 2024 08:31:11 -0700 Subject: [PATCH 20/25] Reverted HashSet constructor improvement since not netstandard2.0 compatible --- src/FSharp.Core/array.fs | 2 +- src/FSharp.Core/list.fs | 2 +- src/FSharp.Core/seq.fs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 34c89a6cec9..a18075d1b3e 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2082,7 +2082,7 @@ module Array = result[i] <- pool[j] pool[j] <- pool[inputLength - i - 1] else - let selected = HashSet(count) + let selected = HashSet() for i = 0 to count - 1 do let mutable j = random.Next(0, inputLength) diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 5ad7a8fed5e..143e772d0c0 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1143,7 +1143,7 @@ module List = item ] else - let selected = HashSet(count) + let selected = HashSet() [ for _ = 0 to count - 1 do diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 801f5d7b53f..584d4ef8e2d 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2107,7 +2107,7 @@ module Seq = item } else - let selected = HashSet(count) + let selected = HashSet() seq { for _ = 0 to count - 1 do let mutable j = From 17e0cfb44dc70de28c80bdff87053949ace9859a Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 12 Jun 2024 09:49:58 -0700 Subject: [PATCH 21/25] Fix formatting --- src/FSharp.Core/seq.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 584d4ef8e2d..3c6c19fb848 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2108,6 +2108,7 @@ module Seq = } else let selected = HashSet() + seq { for _ = 0 to count - 1 do let mutable j = From 2334d188290d59b19af3ca3b2f7ee997e6251f9f Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Thu, 13 Jun 2024 16:18:26 -0700 Subject: [PATCH 22/25] Fixed nan case for randomizer function --- src/FSharp.Core/local.fs | 5 ++- .../ArrayModule.fs | 45 ++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index 52c06ea6ebc..b4c797b221d 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1207,12 +1207,13 @@ module internal Random = let private executeRandomizer (randomizer: unit -> float) = let value = randomizer() - if value < 0.0 || value >= 1.0 then + if value >= 0.0 && value < 1.0 then + value + else let argName = nameof randomizer invalidArgOutOfRangeFmt argName "{0}\n{1} returned {2}, should be in range [0.0, 1.0)." [|SR.GetString SR.outOfRange; argName; value|] - value let next (randomizer: unit -> float) (minValue: int) (maxValue: int) = int ((executeRandomizer randomizer) * float (maxValue - minValue)) + minValue diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index d6285ff6160..914ee3558ff 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -2023,11 +2023,16 @@ type ArrayModule() = member _.RandomShuffleByWrongArg() = let nullArr = null let arr = [| 1..20 |] - let wrongRandomizer = fun () -> 1.0 + let wrongRandomizers = [ + fun () -> nan + fun () -> 1.0 + fun () -> infinity + ] let randomizer = Random(123).NextDouble CheckThrowsArgumentNullException (fun () -> Array.randomShuffleBy randomizer nullArr |> ignore) - CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleBy wrongRandomizer arr |> ignore) + wrongRandomizers |> List.iter (fun wrongRandomizer -> + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleBy wrongRandomizer arr |> ignore)) [] member _.RandomShuffleInPlace() = @@ -2101,11 +2106,16 @@ type ArrayModule() = member _.RandomShuffleInPlaceByWrongArg() = let nullArr = null let arr = [| 1..20 |] - let wrongRandomizer = fun () -> 1.0 + let wrongRandomizers = [ + fun () -> nan + fun () -> 1.0 + fun () -> infinity + ] let randomizer = Random(123).NextDouble CheckThrowsArgumentNullException (fun () -> Array.randomShuffleInPlaceBy randomizer nullArr |> ignore) - CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleInPlaceBy wrongRandomizer arr |> ignore) + wrongRandomizers |> List.iter (fun wrongRandomizer -> + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomShuffleInPlaceBy wrongRandomizer arr |> ignore)) [] member _.RandomChoice() = @@ -2175,11 +2185,16 @@ type ArrayModule() = let nullArr = null let emptyArr = [||] let arr = [| 1..20 |] - let wrongRandomizer = fun () -> 1.0 + let wrongRandomizers = [ + fun () -> nan + fun () -> 1.0 + fun () -> infinity + ] let randomizer = Random(123).NextDouble CheckThrowsArgumentNullException (fun () -> Array.randomChoiceBy randomizer nullArr |> ignore) - CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoiceBy wrongRandomizer arr |> ignore) + wrongRandomizers |> List.iter (fun wrongRandomizer -> + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoiceBy wrongRandomizer arr |> ignore)) CheckThrowsArgumentException (fun () -> Array.randomChoiceBy randomizer emptyArr |> ignore) [] @@ -2261,13 +2276,18 @@ type ArrayModule() = let nullArr = null let emptyArr = [||] let arr = [| 1..50 |] - let wrongRandomizer = fun () -> 1.0 + let wrongRandomizers = [ + fun () -> nan + fun () -> 1.0 + fun () -> infinity + ] let randomizer = Random(123).NextDouble let choicesLength = 20 let negativeChoicesLength = -1 CheckThrowsArgumentNullException (fun () -> Array.randomChoicesBy randomizer choicesLength nullArr |> ignore) - CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoicesBy wrongRandomizer choicesLength arr |> ignore) + wrongRandomizers |> List.iter (fun wrongRandomizer -> + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomChoicesBy wrongRandomizer choicesLength arr |> ignore)) CheckThrowsArgumentException (fun () -> Array.randomChoicesBy randomizer choicesLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomChoicesBy randomizer negativeChoicesLength arr |> ignore) @@ -2359,14 +2379,19 @@ type ArrayModule() = let nullArr = null let emptyArr = [||] let arr = [| 1..50 |] - let wrongRandomizer = fun () -> 1.0 + let wrongRandomizers = [ + fun () -> nan + fun () -> 1.0 + fun () -> infinity + ] let randomizer = Random(123).NextDouble let tooBigSampleLength = 100 let negativeSampleLength = -1 let sampleLength = 20 CheckThrowsArgumentNullException (fun () -> Array.randomSampleBy randomizer sampleLength nullArr |> ignore) - CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomSampleBy wrongRandomizer sampleLength arr |> ignore) + wrongRandomizers |> List.iter (fun wrongRandomizer -> + CheckThrowsArgumentOutOfRangeException (fun () -> Array.randomSampleBy wrongRandomizer sampleLength arr |> ignore)) CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer sampleLength emptyArr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer negativeSampleLength arr |> ignore) CheckThrowsArgumentException (fun () -> Array.randomSampleBy randomizer tooBigSampleLength arr |> ignore) \ No newline at end of file From ccee921099c08115f7e17f00cf8330001e8a4084 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 19 Jun 2024 12:34:59 -0700 Subject: [PATCH 23/25] PR review fixes --- src/FSharp.Core/array.fs | 1 - src/FSharp.Core/array.fsi | 6 +++--- src/FSharp.Core/list.fs | 1 - src/FSharp.Core/list.fsi | 6 +++--- src/FSharp.Core/local.fs | 2 +- src/FSharp.Core/seq.fs | 1 - src/FSharp.Core/seq.fsi | 8 ++++---- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index a18075d1b3e..fb1a44c2415 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2070,7 +2070,6 @@ module Array = let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count - // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 8880bc59218..8947a2a8d07 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3354,7 +3354,7 @@ module Array = /// Thrown when the input array is null. /// Thrown when the input array is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input array. + /// Thrown when count is greater than the length of the input array. /// /// /// @@ -3379,7 +3379,7 @@ module Array = /// Thrown when the random argument is null. /// Thrown when the input array is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input array. + /// Thrown when count is greater than the length of the input array. /// /// /// @@ -3403,7 +3403,7 @@ module Array = /// Thrown when the input array is null. /// Thrown when the input array is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input array. + /// Thrown when count is greater than the length of the input array. /// Thrown when the randomizer function returns a value outside the range [0, 1). /// /// diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 143e772d0c0..816bb9562d9 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1126,7 +1126,6 @@ module List = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index 3a3839236cf..5393fd14439 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -2889,7 +2889,7 @@ module List = /// /// Thrown when the input list is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input list. + /// Thrown when count is greater than the length of the input list. /// /// /// @@ -2913,7 +2913,7 @@ module List = /// Thrown when the random argument is null. /// Thrown when the input list is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input list. + /// Thrown when count is greater than the length of the input list. /// /// /// @@ -2936,7 +2936,7 @@ module List = /// /// Thrown when the input list is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input list. + /// Thrown when count is greater than the length of the input list. /// Thrown when the randomizer function returns a value outside the range [0, 1). /// /// diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index b4c797b221d..2924be2b488 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - +// Copyright © 2001-2023 Python Software Foundation. All Rights Reserved. License: https://docs.python.org/3/license.html#psf-license. Code for getMaxSetSizeForSampling is taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 namespace Microsoft.FSharp.Core diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 3c6c19fb848..0beaa95d700 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2092,7 +2092,6 @@ module Seq = if count >= inputLength then invalidArgOutOfRange "count" count "source.Length" inputLength - // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index d190d74d24a..c1e5a6f0ba9 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2936,7 +2936,7 @@ module Seq = [] val insertManyAt: index: int -> values: seq<'T> -> source: seq<'T> -> seq<'T> - /// Return a new sequence shuffled a in random order. + /// Return a new sequence shuffled in a random order. /// /// The input sequence. /// @@ -3155,7 +3155,7 @@ module Seq = /// Thrown when the input sequence is null. /// Thrown when the input sequence is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input sequence. + /// Thrown when count is greater than the length of the input sequence. /// /// /// @@ -3182,7 +3182,7 @@ module Seq = /// Thrown when the random argument is null. /// Thrown when the input sequence is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input sequence. + /// Thrown when count is greater than the length of the input sequence. /// /// /// @@ -3208,7 +3208,7 @@ module Seq = /// Thrown when the input sequence is null. /// Thrown when the input sequence is empty. /// Thrown when count is less than 0. - /// Thrown when count is greater than the length of the input sequence. + /// Thrown when count is greater than the length of the input sequence. /// Thrown when the randomizer function returns a number outside the range [0.0..1.0). /// /// From f832333ca7cf9c2ef55e1b3be899e15af3788ab8 Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 26 Jun 2024 14:41:42 +0300 Subject: [PATCH 24/25] PR review changes --- src/FSharp.Core/array.fs | 4 ++-- src/FSharp.Core/list.fs | 4 ++-- src/FSharp.Core/seq.fs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index fb1a44c2415..5e4e3e496f1 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2066,7 +2066,7 @@ module Array = invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString if count > inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count @@ -2106,7 +2106,7 @@ module Array = invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString if count >= inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 816bb9562d9..2001301c1ce 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1084,7 +1084,7 @@ module List = invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString if count >= inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = @@ -1124,7 +1124,7 @@ module List = invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString if count >= inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 0beaa95d700..731dcd2d83e 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2049,7 +2049,7 @@ module Seq = invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString if count >= inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 let setSize = @@ -2090,7 +2090,7 @@ module Seq = invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString if count >= inputLength then - invalidArgOutOfRange "count" count "source.Length" inputLength + invalidArg "count" (SR.GetString(SR.notEnoughElements)) let setSize = Microsoft.FSharp.Primitives.Basics.Random.getMaxSetSizeForSampling count From 3f35e7a3334acc115b775f8d9c770b09d3d536af Mon Sep 17 00:00:00 2001 From: Vladimir Shchur Date: Wed, 26 Jun 2024 14:46:24 +0300 Subject: [PATCH 25/25] Fixed input length check logic for sample --- src/FSharp.Core/array.fs | 2 +- src/FSharp.Core/list.fs | 4 ++-- src/FSharp.Core/seq.fs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 5e4e3e496f1..77153012a5e 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -2105,7 +2105,7 @@ module Array = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputArrayEmptyString - if count >= inputLength then + if count > inputLength then invalidArg "count" (SR.GetString(SR.notEnoughElements)) let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index 2001301c1ce..d502107a6da 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -1083,7 +1083,7 @@ module List = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString - if count >= inputLength then + if count > inputLength then invalidArg "count" (SR.GetString(SR.notEnoughElements)) // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 @@ -1123,7 +1123,7 @@ module List = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString - if count >= inputLength then + if count > inputLength then invalidArg "count" (SR.GetString(SR.notEnoughElements)) let setSize = diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 731dcd2d83e..35adcdda557 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -2048,7 +2048,7 @@ module Seq = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString - if count >= inputLength then + if count > inputLength then invalidArg "count" (SR.GetString(SR.notEnoughElements)) // algorithm taken from https://github.com/python/cpython/blob/69b3e8ea569faabccd74036e3d0e5ec7c0c62a20/Lib/random.py#L363-L456 @@ -2089,7 +2089,7 @@ module Seq = if inputLength = 0 then invalidArg "source" LanguagePrimitives.ErrorStrings.InputSequenceEmptyString - if count >= inputLength then + if count > inputLength then invalidArg "count" (SR.GetString(SR.notEnoughElements)) let setSize =