github.com/onflow/flow-go/crypto@v0.24.8/random/rand_utils.go (about) 1 package random 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 "gonum.org/v1/gonum/stat" 10 ) 11 12 // BasicDistributionTest is a test function to run a basic statistic test on `randf` output. 13 // `randf` is a function that outputs random integers. 14 // It partitions all outputs into `n` continuous classes and computes the distribution 15 // over the partition. Each class has a width of `classWidth`: first class is [0..classWidth-1], 16 // secons class is [classWidth..2*classWidth-1], etc.. 17 // It computes the frequency of outputs in the `n` classes and computes the 18 // standard deviation of frequencies. A small standard deviation is a necessary 19 // condition for a uniform distribution of `randf` (though is not a guarantee of 20 // uniformity) 21 func BasicDistributionTest(t *testing.T, n uint64, classWidth uint64, randf func() (uint64, error)) { 22 // sample size should ideally be a high number multiple of `n` 23 // but if `n` is too small, we could use a small sample size so that the test 24 // isn't too slow 25 sampleSize := 1000 * n 26 if n < 100 { 27 sampleSize = (80000 / n) * n // highest multiple of n less than 80000 28 } 29 distribution := make([]float64, n) 30 // populate the distribution 31 for i := uint64(0); i < sampleSize; i++ { 32 r, err := randf() 33 require.NoError(t, err) 34 if n*classWidth != 0 { 35 require.Less(t, r, n*classWidth) 36 } 37 distribution[r/classWidth] += 1.0 38 } 39 EvaluateDistributionUniformity(t, distribution) 40 } 41 42 // EvaluateDistributionUniformity evaluates if the input distribution is close to uinform 43 // through a basic quick test. 44 // The test computes the standard deviation and checks it is small enough compared 45 // to the distribution mean. 46 func EvaluateDistributionUniformity(t *testing.T, distribution []float64) { 47 tolerance := 0.05 48 stdev := stat.StdDev(distribution, nil) 49 mean := stat.Mean(distribution, nil) 50 assert.Greater(t, tolerance*mean, stdev, fmt.Sprintf("basic randomness test failed: n: %d, stdev: %v, mean: %v", len(distribution), stdev, mean)) 51 }