Skip to content

Commit

Permalink
math/rand/v2: start of new API
Browse files Browse the repository at this point in the history
This is the beginning of the math/rand/v2 package from proposal #61716.
Start by copying old API. This CL copies math/rand/* to math/rand/v2
and updates references to math/rand to add v2 throughout.
Later CLs will make the v2 changes.

For #61716.

Change-Id: I1624ccffae3dfa442d4ba2461942decbd076e11b
Reviewed-on: https://go-review.googlesource.com/c/go/+/502495
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
  • Loading branch information
rsc authored and gopherbot committed Oct 30, 2023
1 parent 8c92897 commit 59f0ab4
Show file tree
Hide file tree
Showing 16 changed files with 2,885 additions and 1 deletion.
47 changes: 47 additions & 0 deletions api/next/61716.txt
@@ -0,0 +1,47 @@
pkg math/rand/v2, func ExpFloat64() float64 #61716
pkg math/rand/v2, func Float32() float32 #61716
pkg math/rand/v2, func Float64() float64 #61716
pkg math/rand/v2, func Int() int #61716
pkg math/rand/v2, func Int31() int32 #61716
pkg math/rand/v2, func Int31n(int32) int32 #61716
pkg math/rand/v2, func Int63() int64 #61716
pkg math/rand/v2, func Int63n(int64) int64 #61716
pkg math/rand/v2, func Intn(int) int #61716
pkg math/rand/v2, func New(Source) *Rand #61716
pkg math/rand/v2, func NewSource(int64) Source #61716
pkg math/rand/v2, func NewZipf(*Rand, float64, float64, uint64) *Zipf #61716
pkg math/rand/v2, func NormFloat64() float64 #61716
pkg math/rand/v2, func Perm(int) []int #61716
pkg math/rand/v2, func Read //deprecated #61716
pkg math/rand/v2, func Read([]uint8) (int, error) #61716
pkg math/rand/v2, func Seed //deprecated #61716
pkg math/rand/v2, func Seed(int64) #61716
pkg math/rand/v2, func Shuffle(int, func(int, int)) #61716
pkg math/rand/v2, func Uint32() uint32 #61716
pkg math/rand/v2, func Uint64() uint64 #61716
pkg math/rand/v2, method (*Rand) ExpFloat64() float64 #61716
pkg math/rand/v2, method (*Rand) Float32() float32 #61716
pkg math/rand/v2, method (*Rand) Float64() float64 #61716
pkg math/rand/v2, method (*Rand) Int() int #61716
pkg math/rand/v2, method (*Rand) Int31() int32 #61716
pkg math/rand/v2, method (*Rand) Int31n(int32) int32 #61716
pkg math/rand/v2, method (*Rand) Int63() int64 #61716
pkg math/rand/v2, method (*Rand) Int63n(int64) int64 #61716
pkg math/rand/v2, method (*Rand) Intn(int) int #61716
pkg math/rand/v2, method (*Rand) NormFloat64() float64 #61716
pkg math/rand/v2, method (*Rand) Perm(int) []int #61716
pkg math/rand/v2, method (*Rand) Read([]uint8) (int, error) #61716
pkg math/rand/v2, method (*Rand) Seed(int64) #61716
pkg math/rand/v2, method (*Rand) Shuffle(int, func(int, int)) #61716
pkg math/rand/v2, method (*Rand) Uint32() uint32 #61716
pkg math/rand/v2, method (*Rand) Uint64() uint64 #61716
pkg math/rand/v2, method (*Zipf) Uint64() uint64 #61716
pkg math/rand/v2, type Rand struct #61716
pkg math/rand/v2, type Source interface { Int63, Seed } #61716
pkg math/rand/v2, type Source interface, Int63() int64 #61716
pkg math/rand/v2, type Source interface, Seed(int64) #61716
pkg math/rand/v2, type Source64 interface { Int63, Seed, Uint64 } #61716
pkg math/rand/v2, type Source64 interface, Int63() int64 #61716
pkg math/rand/v2, type Source64 interface, Seed(int64) #61716
pkg math/rand/v2, type Source64 interface, Uint64() uint64 #61716
pkg math/rand/v2, type Zipf struct #61716
2 changes: 1 addition & 1 deletion src/go/build/deps_test.go
Expand Up @@ -131,7 +131,7 @@ var depsRules = `
< math/cmplx;
MATH
< math/rand;
< math/rand, math/rand/v2;
MATH
< runtime/metrics;
Expand Down
40 changes: 40 additions & 0 deletions src/math/rand/v2/auto_test.go
@@ -0,0 +1,40 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rand_test

import (
. "math/rand/v2"
"testing"
)

// This test is first, in its own file with an alphabetically early name,
// to try to make sure that it runs early. It has the best chance of
// detecting deterministic seeding if it's the first test that runs.

func TestAuto(t *testing.T) {
// Pull out 10 int64s from the global source
// and then check that they don't appear in that
// order in the deterministic Seed(1) result.
var out []int64
for i := 0; i < 10; i++ {
out = append(out, Int63())
}

// Look for out in Seed(1)'s output.
// Strictly speaking, we should look for them in order,
// but this is good enough and not significantly more
// likely to have a false positive.
Seed(1)
found := 0
for i := 0; i < 1000; i++ {
x := Int63()
if x == out[found] {
found++
if found == len(out) {
t.Fatalf("found unseeded output in Seed(1) output")
}
}
}
}
148 changes: 148 additions & 0 deletions src/math/rand/v2/default_test.go
@@ -0,0 +1,148 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rand_test

import (
"fmt"
"internal/race"
"internal/testenv"
. "math/rand/v2"
"os"
"runtime"
"strconv"
"sync"
"testing"
)

// Test that racy access to the default functions behaves reasonably.
func TestDefaultRace(t *testing.T) {
// Skip the test in short mode, but even in short mode run
// the test if we are using the race detector, because part
// of this is to see whether the race detector reports any problems.
if testing.Short() && !race.Enabled {
t.Skip("skipping starting another executable in short mode")
}

const env = "GO_RAND_TEST_HELPER_CODE"
if v := os.Getenv(env); v != "" {
doDefaultTest(t, v)
return
}

t.Parallel()

for i := 0; i < 6; i++ {
i := i
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Parallel()
exe, err := os.Executable()
if err != nil {
exe = os.Args[0]
}
cmd := testenv.Command(t, exe, "-test.run=TestDefaultRace")
cmd = testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, fmt.Sprintf("GO_RAND_TEST_HELPER_CODE=%d", i/2))
if i%2 != 0 {
cmd.Env = append(cmd.Env, "GODEBUG=randautoseed=0")
}
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
}
if err != nil {
t.Error(err)
}
})
}
}

// doDefaultTest should be run before there have been any calls to the
// top-level math/rand functions. Make sure that we can make concurrent
// calls to top-level functions and to Seed without any duplicate values.
// This will also give the race detector a change to report any problems.
func doDefaultTest(t *testing.T, v string) {
code, err := strconv.Atoi(v)
if err != nil {
t.Fatalf("internal error: unrecognized code %q", v)
}

goroutines := runtime.GOMAXPROCS(0)
if goroutines < 4 {
goroutines = 4
}

ch := make(chan uint64, goroutines*3)
var wg sync.WaitGroup

// The various tests below should not cause race detector reports
// and should not produce duplicate results.
//
// Note: these tests can theoretically fail when using fastrand64
// in that it is possible to coincidentally get the same random
// number twice. That could happen something like 1 / 2**64 times,
// which is rare enough that it may never happen. We don't worry
// about that case.

switch code {
case 0:
// Call Seed and Uint64 concurrently.
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func(s int64) {
defer wg.Done()
Seed(s)
}(int64(i) + 100)
}
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func() {
defer wg.Done()
ch <- Uint64()
}()
}
case 1:
// Call Uint64 concurrently with no Seed.
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func() {
defer wg.Done()
ch <- Uint64()
}()
}
case 2:
// Start with Uint64 to pick the fast source, then call
// Seed and Uint64 concurrently.
ch <- Uint64()
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func(s int64) {
defer wg.Done()
Seed(s)
}(int64(i) + 100)
}
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func() {
defer wg.Done()
ch <- Uint64()
}()
}
default:
t.Fatalf("internal error: unrecognized code %d", code)
}

go func() {
wg.Wait()
close(ch)
}()

m := make(map[uint64]bool)
for i := range ch {
if m[i] {
t.Errorf("saw %d twice", i)
}
m[i] = true
}
}
133 changes: 133 additions & 0 deletions src/math/rand/v2/example_test.go
@@ -0,0 +1,133 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rand_test

import (
"fmt"
"math/rand/v2"
"os"
"strings"
"text/tabwriter"
)

// These tests serve as an example but also make sure we don't change
// the output of the random number generator when given a fixed seed.

func Example() {
answers := []string{
"It is certain",
"It is decidedly so",
"Without a doubt",
"Yes definitely",
"You may rely on it",
"As I see it yes",
"Most likely",
"Outlook good",
"Yes",
"Signs point to yes",
"Reply hazy try again",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"My reply is no",
"My sources say no",
"Outlook not so good",
"Very doubtful",
}
fmt.Println("Magic 8-Ball says:", answers[rand.Intn(len(answers))])
}

// This example shows the use of each of the methods on a *Rand.
// The use of the global functions is the same, without the receiver.
func Example_rand() {
// Create and seed the generator.
// Typically a non-fixed seed should be used, such as time.Now().UnixNano().
// Using a fixed seed will produce the same output on every run.
r := rand.New(rand.NewSource(99))

// The tabwriter here helps us generate aligned output.
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
defer w.Flush()
show := func(name string, v1, v2, v3 any) {
fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", name, v1, v2, v3)
}

// Float32 and Float64 values are in [0, 1).
show("Float32", r.Float32(), r.Float32(), r.Float32())
show("Float64", r.Float64(), r.Float64(), r.Float64())

// ExpFloat64 values have an average of 1 but decay exponentially.
show("ExpFloat64", r.ExpFloat64(), r.ExpFloat64(), r.ExpFloat64())

// NormFloat64 values have an average of 0 and a standard deviation of 1.
show("NormFloat64", r.NormFloat64(), r.NormFloat64(), r.NormFloat64())

// Int31, Int63, and Uint32 generate values of the given width.
// The Int method (not shown) is like either Int31 or Int63
// depending on the size of 'int'.
show("Int31", r.Int31(), r.Int31(), r.Int31())
show("Int63", r.Int63(), r.Int63(), r.Int63())
show("Uint32", r.Uint32(), r.Uint32(), r.Uint32())

// Intn, Int31n, and Int63n limit their output to be < n.
// They do so more carefully than using r.Int()%n.
show("Intn(10)", r.Intn(10), r.Intn(10), r.Intn(10))
show("Int31n(10)", r.Int31n(10), r.Int31n(10), r.Int31n(10))
show("Int63n(10)", r.Int63n(10), r.Int63n(10), r.Int63n(10))

// Perm generates a random permutation of the numbers [0, n).
show("Perm", r.Perm(5), r.Perm(5), r.Perm(5))
// Output:
// Float32 0.2635776 0.6358173 0.6718283
// Float64 0.628605430454327 0.4504798828572669 0.9562755949377957
// ExpFloat64 0.3362240648200941 1.4256072328483647 0.24354758816173044
// NormFloat64 0.17233959114940064 1.577014951434847 0.04259129641113857
// Int31 1501292890 1486668269 182840835
// Int63 3546343826724305832 5724354148158589552 5239846799706671610
// Uint32 2760229429 296659907 1922395059
// Intn(10) 1 2 5
// Int31n(10) 4 7 8
// Int63n(10) 7 6 3
// Perm [1 4 2 3 0] [4 2 1 3 0] [1 2 4 0 3]
}

func ExamplePerm() {
for _, value := range rand.Perm(3) {
fmt.Println(value)
}

// Unordered output: 1
// 2
// 0
}

func ExampleShuffle() {
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {
words[i], words[j] = words[j], words[i]
})
fmt.Println(words)
}

func ExampleShuffle_slicesInUnison() {
numbers := []byte("12345")
letters := []byte("ABCDE")
// Shuffle numbers, swapping corresponding entries in letters at the same time.
rand.Shuffle(len(numbers), func(i, j int) {
numbers[i], numbers[j] = numbers[j], numbers[i]
letters[i], letters[j] = letters[j], letters[i]
})
for i := range numbers {
fmt.Printf("%c: %c\n", letters[i], numbers[i])
}
}

func ExampleIntn() {
fmt.Println(rand.Intn(100))
fmt.Println(rand.Intn(100))
fmt.Println(rand.Intn(100))
}

0 comments on commit 59f0ab4

Please sign in to comment.