traefik/vendor/github.com/BurntSushi/ty/fun/rand.go
2017-03-09 13:13:02 +01:00

94 lines
2.4 KiB
Go

package fun
import (
"math/rand"
"reflect"
"time"
"github.com/BurntSushi/ty"
)
var randNumGen *rand.Rand
func init() {
randNumGen = rand.New(rand.NewSource(time.Now().UnixNano()))
}
// ShuffleGen has a parametric type:
//
// func ShuffleGen(xs []A, rng *rand.Rand)
//
// ShuffleGen shuffles `xs` in place using the given random number
// generator `rng`.
func ShuffleGen(xs interface{}, rng *rand.Rand) {
chk := ty.Check(
new(func([]ty.A, *rand.Rand)),
xs, rng)
vxs := chk.Args[0]
// Implements the Fisher-Yates shuffle: http://goo.gl/Hb9vg
xsLen := vxs.Len()
swapper := swapperOf(vxs.Type().Elem())
for i := xsLen - 1; i >= 1; i-- {
j := rng.Intn(i + 1)
swapper.swap(vxs.Index(i), vxs.Index(j))
}
}
// Shuffle has a parametric type:
//
// func Shuffle(xs []A)
//
// Shuffle shuffles `xs` in place using a default random number
// generator seeded once at program initialization.
func Shuffle(xs interface{}) {
ShuffleGen(xs, randNumGen)
}
// Sample has a parametric type:
//
// func Sample(population []A, n int) []A
//
// Sample returns a random sample of size `n` from a list
// `population` using a default random number generator seeded once at
// program initialization.
// All elements in `population` have an equal chance of being selected.
// If `n` is greater than the size of `population`, then `n` is set to
// the size of the population.
func Sample(population interface{}, n int) interface{} {
return SampleGen(population, n, randNumGen)
}
// SampleGen has a parametric type:
//
// func SampleGen(population []A, n int, rng *rand.Rand) []A
//
// SampleGen returns a random sample of size `n` from a list
// `population` using a given random number generator `rng`.
// All elements in `population` have an equal chance of being selected.
// If `n` is greater than the size of `population`, then `n` is set to
// the size of the population.
func SampleGen(population interface{}, n int, rng *rand.Rand) interface{} {
chk := ty.Check(
new(func([]ty.A, int, *rand.Rand) []ty.A),
population, n, rng)
rpop, tsamp := chk.Args[0], chk.Returns[0]
popLen := rpop.Len()
if n == 0 {
return reflect.MakeSlice(tsamp, 0, 0).Interface()
}
if n > popLen {
n = popLen
}
// TODO(burntsushi): Implement an algorithm that doesn't depend on
// the size of the population.
rsamp := reflect.MakeSlice(tsamp, n, n)
choices := rng.Perm(popLen)
for i := 0; i < n; i++ {
rsamp.Index(i).Set(rpop.Index(choices[i]))
}
return rsamp.Interface()
}