Skip to content

Commit e431d94

Browse files
committed
[All] Added random probability distribution tests
1 parent 9f4747e commit e431d94

5 files changed

Lines changed: 311 additions & 0 deletions

File tree

tests/Beam/RandomTests.fs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,57 @@ open System
44
open Fable.Tests.Util
55
open Util.Testing
66

7+
let chiSquaredStatistic bucketCount sampleCount (nextBucket: unit -> int) =
8+
let counts = Array.zeroCreate bucketCount
9+
10+
for _ in 1 .. sampleCount do
11+
let bucket = nextBucket()
12+
counts[bucket] <- counts[bucket] + 1
13+
14+
let expected = float sampleCount / float bucketCount
15+
16+
counts
17+
|> Array.sumBy (fun count ->
18+
let diff = float count - expected
19+
diff * diff / expected)
20+
21+
let chiSquaredUpperBound degreesOfFreedom sigma =
22+
float degreesOfFreedom + sigma * Math.Sqrt(2.0 * float degreesOfFreedom)
23+
24+
let runsZScore sampleCount (nextBit: unit -> bool) =
25+
let mutable ones = 0
26+
let mutable zeros = 0
27+
let mutable runs = 0
28+
let mutable hasPrevious = false
29+
let mutable previous = false
30+
31+
for _ in 1 .. sampleCount do
32+
let current = nextBit()
33+
34+
if current then
35+
ones <- ones + 1
36+
else
37+
zeros <- zeros + 1
38+
39+
if not hasPrevious || current <> previous then
40+
runs <- runs + 1
41+
previous <- current
42+
hasPrevious <- true
43+
44+
if ones = 0 || zeros = 0 then
45+
Double.PositiveInfinity
46+
else
47+
let ones' = float ones
48+
let zeros' = float zeros
49+
let total = float sampleCount
50+
let expectedRuns = 1.0 + 2.0 * ones' * zeros' / total
51+
52+
let variance =
53+
(2.0 * ones' * zeros' * (2.0 * ones' * zeros' - ones' - zeros'))
54+
/ (total * total * (total - 1.0))
55+
56+
Math.Abs(float runs - expectedRuns) / Math.Sqrt(variance)
57+
758
[<Fact>]
859
let ``test System.Random works`` () =
960
let rnd = Random()
@@ -54,6 +105,18 @@ let ``test System.Random seeded validates arguments`` () =
54105
throwsAnyError <| fun () -> rnd.Next(14, 10)
55106
// throwsAnyError <| fun () -> rnd.NextBytes(null)
56107

108+
[<Fact>]
109+
let ``test System.Random seeded passes chi-squared test`` () =
110+
let rnd = Random(2026)
111+
let statistic = chiSquaredStatistic 16 10000 (fun () -> rnd.Next(16))
112+
statistic < chiSquaredUpperBound 15 4.0 |> equal true
113+
114+
[<Fact>]
115+
let ``test System.Random seeded passes Wald-Wolfowitz runs test`` () =
116+
let rnd = Random(2027)
117+
let zScore = runsZScore 10000 (fun () -> rnd.NextDouble() >= 0.5)
118+
zScore < 4.0 |> equal true
119+
57120
[<Fact>]
58121
let ``test System.Random.NextBytes works`` () =
59122
let buffer = Array.create 16 0uy

tests/Dart/src/RandomTests.fs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,57 @@ module Fable.Tests.Dart.RandomTests
33
open Util
44
open System
55

6+
let chiSquaredStatistic bucketCount sampleCount (nextBucket: unit -> int) =
7+
let counts = Array.zeroCreate bucketCount
8+
9+
for _ in 1 .. sampleCount do
10+
let bucket = nextBucket()
11+
counts[bucket] <- counts[bucket] + 1
12+
13+
let expected = float sampleCount / float bucketCount
14+
15+
counts
16+
|> Array.sumBy (fun count ->
17+
let diff = float count - expected
18+
diff * diff / expected)
19+
20+
let chiSquaredUpperBound degreesOfFreedom sigma =
21+
float degreesOfFreedom + sigma * Math.Sqrt(2.0 * float degreesOfFreedom)
22+
23+
let runsZScore sampleCount (nextBit: unit -> bool) =
24+
let mutable ones = 0
25+
let mutable zeros = 0
26+
let mutable runs = 0
27+
let mutable hasPrevious = false
28+
let mutable previous = false
29+
30+
for _ in 1 .. sampleCount do
31+
let current = nextBit()
32+
33+
if current then
34+
ones <- ones + 1
35+
else
36+
zeros <- zeros + 1
37+
38+
if not hasPrevious || current <> previous then
39+
runs <- runs + 1
40+
previous <- current
41+
hasPrevious <- true
42+
43+
if ones = 0 || zeros = 0 then
44+
Double.PositiveInfinity
45+
else
46+
let ones' = float ones
47+
let zeros' = float zeros
48+
let total = float sampleCount
49+
let expectedRuns = 1.0 + 2.0 * ones' * zeros' / total
50+
51+
let variance =
52+
(2.0 * ones' * zeros' * (2.0 * ones' * zeros' - ones' - zeros'))
53+
/ (total * total * (total - 1.0))
54+
55+
Math.Abs(float runs - expectedRuns) / Math.Sqrt(variance)
56+
657
let tests () =
758

859
testCase "System.Random works" <| fun () ->
@@ -33,6 +84,16 @@ let tests () =
3384
throwsAnyError <| fun () -> rnd.Next(14, 10)
3485
throwsAnyError <| fun () -> rnd.NextBytes(null)
3586

87+
testCase "System.Random seeded passes chi-squared test" <| fun () ->
88+
let rnd = Random(2026)
89+
let statistic = chiSquaredStatistic 16 10000 (fun () -> rnd.Next(16))
90+
statistic < chiSquaredUpperBound 15 4.0 |> equal true
91+
92+
testCase "System.Random seeded passes Wald-Wolfowitz runs test" <| fun () ->
93+
let rnd = Random(2027)
94+
let zScore = runsZScore 10000 (fun () -> rnd.NextDouble() >= 0.5)
95+
zScore < 4.0 |> equal true
96+
3697
testCase "System.Random.NextBytes works" <| fun () ->
3798
let buffer = Array.create 16 0uy
3899
Random().NextBytes(buffer)

tests/Js/Main/RandomTests.fs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,57 @@ open System
44
open Util.Testing
55
open Fable.Tests.Util
66

7+
let chiSquaredStatistic bucketCount sampleCount (nextBucket: unit -> int) =
8+
let counts = Array.zeroCreate bucketCount
9+
10+
for _ in 1 .. sampleCount do
11+
let bucket = nextBucket()
12+
counts[bucket] <- counts[bucket] + 1
13+
14+
let expected = float sampleCount / float bucketCount
15+
16+
counts
17+
|> Array.sumBy (fun count ->
18+
let diff = float count - expected
19+
diff * diff / expected)
20+
21+
let chiSquaredUpperBound degreesOfFreedom sigma =
22+
float degreesOfFreedom + sigma * Math.Sqrt(2.0 * float degreesOfFreedom)
23+
24+
let runsZScore sampleCount (nextBit: unit -> bool) =
25+
let mutable ones = 0
26+
let mutable zeros = 0
27+
let mutable runs = 0
28+
let mutable hasPrevious = false
29+
let mutable previous = false
30+
31+
for _ in 1 .. sampleCount do
32+
let current = nextBit()
33+
34+
if current then
35+
ones <- ones + 1
36+
else
37+
zeros <- zeros + 1
38+
39+
if not hasPrevious || current <> previous then
40+
runs <- runs + 1
41+
previous <- current
42+
hasPrevious <- true
43+
44+
if ones = 0 || zeros = 0 then
45+
Double.PositiveInfinity
46+
else
47+
let ones' = float ones
48+
let zeros' = float zeros
49+
let total = float sampleCount
50+
let expectedRuns = 1.0 + 2.0 * ones' * zeros' / total
51+
52+
let variance =
53+
(2.0 * ones' * zeros' * (2.0 * ones' * zeros' - ones' - zeros'))
54+
/ (total * total * (total - 1.0))
55+
56+
Math.Abs(float runs - expectedRuns) / Math.Sqrt(variance)
57+
758
let tests =
859
testList "Random" [
960
testCase "System.Random works" <| fun () ->
@@ -33,6 +84,16 @@ let tests =
3384
throwsAnyError <| fun () -> rnd.Next(14, 10)
3485
throwsAnyError <| fun () -> rnd.NextBytes(null)
3586

87+
testCase "System.Random seeded passes chi-squared test" <| fun () ->
88+
let rnd = Random(2026)
89+
let statistic = chiSquaredStatistic 16 10000 (fun () -> rnd.Next(16))
90+
statistic < chiSquaredUpperBound 15 4.0 |> equal true
91+
92+
testCase "System.Random seeded passes Wald-Wolfowitz runs test" <| fun () ->
93+
let rnd = Random(2027)
94+
let zScore = runsZScore 10000 (fun () -> rnd.NextDouble() >= 0.5)
95+
zScore < 4.0 |> equal true
96+
3697
// Note: Test could fail sometime during life of universe, if it picks all zeroes.
3798
testCase "System.Random.NextBytes works" <| fun () ->
3899
let buffer = Array.create 16 0uy // guid-sized buffer

tests/Python/TestRandom.fs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,57 @@ open System
44
open Fable.Tests.Util
55
open Util.Testing
66

7+
let chiSquaredStatistic bucketCount sampleCount (nextBucket: unit -> int) =
8+
let counts = Array.zeroCreate bucketCount
9+
10+
for _ in 1 .. sampleCount do
11+
let bucket = nextBucket()
12+
counts[bucket] <- counts[bucket] + 1
13+
14+
let expected = float sampleCount / float bucketCount
15+
16+
counts
17+
|> Array.sumBy (fun count ->
18+
let diff = float count - expected
19+
diff * diff / expected)
20+
21+
let chiSquaredUpperBound degreesOfFreedom sigma =
22+
float degreesOfFreedom + sigma * Math.Sqrt(2.0 * float degreesOfFreedom)
23+
24+
let runsZScore sampleCount (nextBit: unit -> bool) =
25+
let mutable ones = 0
26+
let mutable zeros = 0
27+
let mutable runs = 0
28+
let mutable hasPrevious = false
29+
let mutable previous = false
30+
31+
for _ in 1 .. sampleCount do
32+
let current = nextBit()
33+
34+
if current then
35+
ones <- ones + 1
36+
else
37+
zeros <- zeros + 1
38+
39+
if not hasPrevious || current <> previous then
40+
runs <- runs + 1
41+
previous <- current
42+
hasPrevious <- true
43+
44+
if ones = 0 || zeros = 0 then
45+
Double.PositiveInfinity
46+
else
47+
let ones' = float ones
48+
let zeros' = float zeros
49+
let total = float sampleCount
50+
let expectedRuns = 1.0 + 2.0 * ones' * zeros' / total
51+
52+
let variance =
53+
(2.0 * ones' * zeros' * (2.0 * ones' * zeros' - ones' - zeros'))
54+
/ (total * total * (total - 1.0))
55+
56+
Math.Abs(float runs - expectedRuns) / Math.Sqrt(variance)
57+
758
[<Fact>]
859
let ``test System.Random works`` () =
960
let rnd = Random()
@@ -35,6 +86,18 @@ let ``test System.Random seeded validates arguments`` () =
3586
throwsAnyError <| fun () -> rnd.Next(14, 10)
3687
// throwsAnyError <| fun () -> rnd.NextBytes(null)
3788

89+
[<Fact>]
90+
let ``test System.Random seeded passes chi-squared test`` () =
91+
let rnd = Random(2026)
92+
let statistic = chiSquaredStatistic 16 10000 (fun () -> rnd.Next(16))
93+
statistic < chiSquaredUpperBound 15 4.0 |> equal true
94+
95+
[<Fact>]
96+
let ``test System.Random seeded passes Wald-Wolfowitz runs test`` () =
97+
let rnd = Random(2027)
98+
let zScore = runsZScore 10000 (fun () -> rnd.NextDouble() >= 0.5)
99+
zScore < 4.0 |> equal true
100+
38101
[<Fact>]
39102
let ``test System.Random.NextBytes works`` () =
40103
let buffer = Array.create 16 0uy

tests/Rust/tests/src/RandomTests.fs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,57 @@ module Fable.Tests.RandomTests
33
open System
44
open Util.Testing
55

6+
let chiSquaredStatistic bucketCount sampleCount (nextBucket: unit -> int) =
7+
let counts = Array.zeroCreate bucketCount
8+
9+
for _ in 1 .. sampleCount do
10+
let bucket = nextBucket()
11+
counts[bucket] <- counts[bucket] + 1
12+
13+
let expected = float sampleCount / float bucketCount
14+
15+
counts
16+
|> Array.sumBy (fun count ->
17+
let diff = float count - expected
18+
diff * diff / expected)
19+
20+
let chiSquaredUpperBound degreesOfFreedom sigma =
21+
float degreesOfFreedom + sigma * Math.Sqrt(2.0 * float degreesOfFreedom)
22+
23+
let runsZScore sampleCount (nextBit: unit -> bool) =
24+
let mutable ones = 0
25+
let mutable zeros = 0
26+
let mutable runs = 0
27+
let mutable hasPrevious = false
28+
let mutable previous = false
29+
30+
for _ in 1 .. sampleCount do
31+
let current = nextBit()
32+
33+
if current then
34+
ones <- ones + 1
35+
else
36+
zeros <- zeros + 1
37+
38+
if not hasPrevious || current <> previous then
39+
runs <- runs + 1
40+
previous <- current
41+
hasPrevious <- true
42+
43+
if ones = 0 || zeros = 0 then
44+
Double.PositiveInfinity
45+
else
46+
let ones' = float ones
47+
let zeros' = float zeros
48+
let total = float sampleCount
49+
let expectedRuns = 1.0 + 2.0 * ones' * zeros' / total
50+
51+
let variance =
52+
(2.0 * ones' * zeros' * (2.0 * ones' * zeros' - ones' - zeros'))
53+
/ (total * total * (total - 1.0))
54+
55+
Math.Abs(float runs - expectedRuns) / Math.Sqrt(variance)
56+
657
[<Fact>]
758
let ``System.Random works`` () =
859
let rnd = Random()
@@ -34,6 +85,18 @@ let ``System.Random seeded validates arguments`` () =
3485
throwsAnyError <| fun () -> rnd.Next(14, 10)
3586
// throwsAnyError <| fun () -> rnd.NextBytes(null)
3687

88+
[<Fact>]
89+
let ``System.Random seeded passes chi-squared test`` () =
90+
let rnd = Random(2026)
91+
let statistic = chiSquaredStatistic 16 10000 (fun () -> rnd.Next(16))
92+
statistic < chiSquaredUpperBound 15 4.0 |> equal true
93+
94+
[<Fact>]
95+
let ``System.Random seeded passes Wald-Wolfowitz runs test`` () =
96+
let rnd = Random(2027)
97+
let zScore = runsZScore 10000 (fun () -> rnd.NextDouble() >= 0.5)
98+
zScore < 4.0 |> equal true
99+
37100
[<Fact>]
38101
let ``System.Random.NextBytes works`` () =
39102
let buffer = Array.create 16 0uy

0 commit comments

Comments
 (0)