From f547d7dcac11de7937664413497fed35eb1a03ff Mon Sep 17 00:00:00 2001 From: Morgan Date: Fri, 28 Jun 2024 12:28:00 +0200 Subject: [PATCH] feat(stdlibs): add math/rand (#2455) This PR ports over to Gno the [math/rand/v2](https://pkg.go.dev/math/rand/v2) Go package. It provides pseudo-random number generation. Notable omissions: - the [`N` generic function](https://pkg.go.dev/math/rand/v2@go1.22.4#N) - The [chacha8 random source](https://pkg.go.dev/math/rand/v2@go1.22.4#ChaCha8) (we can use PCG instead; and chacha8 can be implemented later) This PR requires a version bump to go 1.22 to have math/rand/v2 in the standard libraries; otherwise the Native tests won't work. We can rollback the changes to move it to 1.22 by removing the native math/rand implementation, and only using the stdlibs. BREAKING CHANGE: existing usages of math/rand used Go's math/rand; however, in this new standard library we use its v2, so results will be different. Closes #1272; See-also #1267
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- .github/workflows/docs-404-checker.yml | 4 +- .github/workflows/examples.yml | 5 +- CONTRIBUTING.md | 2 +- contribs/gnodev/go.mod | 2 +- contribs/gnofaucet/go.mod | 2 +- contribs/gnokeykc/go.mod | 2 +- contribs/gnomd/go.mod | 2 +- .../local-setup/installation.md | 16 +- .../validators/connect-to-existing-chain.md | 4 +- .../validators/setting-up-a-new-chain.md | 4 +- docs/reference/go-gno-compatibility.md | 5 +- examples/gno.land/p/demo/rand/gno.mod | 3 - examples/gno.land/p/demo/rand/rand.gno | 139 ---- .../gno.land/p/demo/rand/rand0_filetest.gno | 56 -- examples/gno.land/p/demo/rand/rand_test.gno | 49 -- examples/gno.land/r/demo/art/gnoface/gno.mod | 7 +- .../gno.land/r/demo/art/gnoface/gnoface.gno | 14 +- .../r/demo/art/gnoface/gnoface_test.gno | 70 +- examples/gno.land/r/x/manfred_outfmt/gno.mod | 5 +- .../gno.land/r/x/manfred_outfmt/outfmt.gno | 15 +- .../r/x/manfred_outfmt/outfmt_test.gno | 22 +- gnovm/stdlibs/bytes/buffer_test.gno | 4 +- gnovm/stdlibs/bytes/bytes_test.gno | 6 +- gnovm/stdlibs/math/rand/auto_test.gno | 39 + gnovm/stdlibs/math/rand/example_test.gno | 131 ++++ gnovm/stdlibs/math/rand/exp.gno | 222 ++++++ gnovm/stdlibs/math/rand/normal.gno | 157 ++++ gnovm/stdlibs/math/rand/pcg.gno | 121 +++ gnovm/stdlibs/math/rand/pcg_test.gno | 78 ++ gnovm/stdlibs/math/rand/rand.gno | 340 +++++++++ gnovm/stdlibs/math/rand/rand_test.gno | 711 ++++++++++++++++++ gnovm/stdlibs/math/rand/regress_test.gno | 478 ++++++++++++ gnovm/stdlibs/math/rand/zipf.gno | 77 ++ gnovm/stdlibs/sort/sort_test.gno | 12 +- gnovm/tests/files/extern/p1/s2.gno | 2 - gnovm/tests/files/import4.gno | 2 +- gnovm/tests/imports.go | 13 +- go.mod | 2 +- misc/autocounterd/go.mod | 2 +- misc/devdeps/go.mod | 2 +- misc/docs-linter/go.mod | 2 +- misc/loop/go.mod | 2 +- tm2/pkg/libtm/README.md | 2 +- tm2/pkg/libtm/go.mod | 2 +- 44 files changed, 2463 insertions(+), 372 deletions(-) delete mode 100644 examples/gno.land/p/demo/rand/gno.mod delete mode 100644 examples/gno.land/p/demo/rand/rand.gno delete mode 100644 examples/gno.land/p/demo/rand/rand0_filetest.gno delete mode 100644 examples/gno.land/p/demo/rand/rand_test.gno create mode 100644 gnovm/stdlibs/math/rand/auto_test.gno create mode 100644 gnovm/stdlibs/math/rand/example_test.gno create mode 100644 gnovm/stdlibs/math/rand/exp.gno create mode 100644 gnovm/stdlibs/math/rand/normal.gno create mode 100644 gnovm/stdlibs/math/rand/pcg.gno create mode 100644 gnovm/stdlibs/math/rand/pcg_test.gno create mode 100644 gnovm/stdlibs/math/rand/rand.gno create mode 100644 gnovm/stdlibs/math/rand/rand_test.gno create mode 100644 gnovm/stdlibs/math/rand/regress_test.gno create mode 100644 gnovm/stdlibs/math/rand/zipf.gno diff --git a/.github/workflows/docs-404-checker.yml b/.github/workflows/docs-404-checker.yml index 0fa8985366c..fc32fdc435a 100644 --- a/.github/workflows/docs-404-checker.yml +++ b/.github/workflows/docs-404-checker.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: '1.21' + go-version: '1.22' - name: Install dependencies run: go mod download @@ -28,4 +28,4 @@ jobs: run: make -C docs/ build - name: Run linter - run: make -C docs/ lint \ No newline at end of file + run: make -C docs/ lint diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index a01f465f38b..54b3cf7d635 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -20,7 +20,6 @@ jobs: fail-fast: false matrix: goversion: - - "1.21.x" - "1.22.x" runs-on: ubuntu-latest timeout-minutes: 30 @@ -36,7 +35,6 @@ jobs: fail-fast: false matrix: goversion: - - "1.21.x" - "1.22.x" # unittests: TODO: matrix with contracts runs-on: ubuntu-latest @@ -60,7 +58,6 @@ jobs: fail-fast: false matrix: goversion: - - "1.21.x" - "1.22.x" # unittests: TODO: matrix with contracts runs-on: ubuntu-latest @@ -92,4 +89,4 @@ jobs: # Find all directories containing gno.mod file find ./examples -name "gno.mod" -execdir go run "$GNO_CMD" mod tidy \; # Check if there are changes after running gno mod tidy - git diff --exit-code || (echo "Some gno.mod files are not tidy, please run 'make tidy'." && exit 1) \ No newline at end of file + git diff --exit-code || (echo "Some gno.mod files are not tidy, please run 'make tidy'." && exit 1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e041ab18875..bc125a6da73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,7 +56,7 @@ The gno repository is primarily based on Go (Golang) and Gno. The primary tech stack for working on the repository: -- Go (version 1.21+) +- Go (version 1.22+) - make (for using Makefile configurations) It is recommended to work on a Unix environment, as most of the tooling is built around ready-made tools in Unix (WSL2 diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index b7a757c36a5..e36ca960bc0 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.21 +go 1.22 replace github.com/gnolang/gno => ../.. diff --git a/contribs/gnofaucet/go.mod b/contribs/gnofaucet/go.mod index dba13876eb7..44ef29aa82a 100644 --- a/contribs/gnofaucet/go.mod +++ b/contribs/gnofaucet/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnofaucet -go 1.21 +go 1.22 require ( github.com/gnolang/faucet v0.2.1 diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod index 4a45cbc83f2..9dc122ffb56 100644 --- a/contribs/gnokeykc/go.mod +++ b/contribs/gnokeykc/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnokeykc -go 1.21 +go 1.22 replace github.com/gnolang/gno => ../.. diff --git a/contribs/gnomd/go.mod b/contribs/gnomd/go.mod index 1c22a07da31..0aae2564395 100644 --- a/contribs/gnomd/go.mod +++ b/contribs/gnomd/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnomd -go 1.21 +go 1.22 require github.com/MichaelMure/go-term-markdown v0.1.4 diff --git a/docs/getting-started/local-setup/installation.md b/docs/getting-started/local-setup/installation.md index 8700ff9a2b2..a3658fa6ab3 100644 --- a/docs/getting-started/local-setup/installation.md +++ b/docs/getting-started/local-setup/installation.md @@ -5,15 +5,15 @@ id: installation # Installation ## Overview -In this tutorial, you will learn how to set up the Gno development environment -locally, so you can get up and running writing Gno code. You will download and +In this tutorial, you will learn how to set up the Gno development environment +locally, so you can get up and running writing Gno code. You will download and install all the necessary tooling, and validate that it is correctly configured to run on your machine. ## Prerequisites - **Git** - **`make` (for running Makefiles)** -- **Go 1.21+** +- **Go 1.22+** - **Go Environment Setup**: - Make sure `$GOPATH` is well-defined, and `$GOPATH/bin` is added to your `$PATH` variable. - To do this, you can add the following line to your `.bashrc`, `.zshrc` or other config file: @@ -30,7 +30,7 @@ GitHub repository somewhere on disk: git clone https://github.com/gnolang/gno.git ``` -## 2. Installing the required tools +## 2. Installing the required tools There are three tools that should be used for getting started with Gno development: - `gno` - the GnoVM binary @@ -42,7 +42,7 @@ To install all three tools, simply run the following in the root of the repo: make install ``` -## 3. Verifying installation +## 3. Verifying installation ### `gno` `gno` provides ample functionality to the user, among which is running, @@ -59,7 +59,7 @@ You should get the help output from the command: ![gno help](../../assets/getting-started/local-setup/local-setup/gno-help.gif) -Alternatively, if you don't want to have the binary callable system-wide, you +Alternatively, if you don't want to have the binary callable system-wide, you can run the binary directly: ```bash @@ -68,8 +68,8 @@ go run ./cmd/gno --help ``` ### `gnodev` -`gnodev` is the go-to Gno development helper tool - it comes with a built in -Gno.land node, a `gnoweb` server to display the state of your smart contracts +`gnodev` is the go-to Gno development helper tool - it comes with a built in +Gno.land node, a `gnoweb` server to display the state of your smart contracts (realms), and a watcher system to actively track changes in your code. Read more about `gnodev` [here](../../gno-tooling/cli/gnodev.md). diff --git a/docs/gno-infrastructure/validators/connect-to-existing-chain.md b/docs/gno-infrastructure/validators/connect-to-existing-chain.md index 42c8cdd909b..5393b05d652 100644 --- a/docs/gno-infrastructure/validators/connect-to-existing-chain.md +++ b/docs/gno-infrastructure/validators/connect-to-existing-chain.md @@ -12,7 +12,7 @@ In this tutorial, you will learn how to start a local Gno node and connect to an - **Git** - **`make` (for running Makefiles)** -- **Go 1.21+** +- **Go 1.22+** - **Go Environment Setup**: Ensure you have Go set up as outlined in the [Go official installation documentation](https://go.dev/doc/install) for your environment @@ -107,4 +107,4 @@ gnoland start \ That's it! 🎉 -Your new Gno node should be up and running, and syncing block data from the remote chain. \ No newline at end of file +Your new Gno node should be up and running, and syncing block data from the remote chain. diff --git a/docs/gno-infrastructure/validators/setting-up-a-new-chain.md b/docs/gno-infrastructure/validators/setting-up-a-new-chain.md index 1927679db8f..8f94037dc1d 100644 --- a/docs/gno-infrastructure/validators/setting-up-a-new-chain.md +++ b/docs/gno-infrastructure/validators/setting-up-a-new-chain.md @@ -13,7 +13,7 @@ Additionally, you will see the different options you can use to make your Gno in - **Git** - **`make` (for running Makefiles)** -- **Go 1.21+** +- **Go 1.22+** - **Go Environment Setup**: Ensure you have Go set up as outlined in the [Go official installation documentation](https://go.dev/doc/install) for your environment @@ -451,4 +451,4 @@ Genesis block generation happens only once during the lifetime of a Gno chain. This means that if you specify a balances file using `gnoland start`, and the chain has already started (advanced from block 0), the specified balance sheet will not be applied. -::: \ No newline at end of file +::: diff --git a/docs/reference/go-gno-compatibility.md b/docs/reference/go-gno-compatibility.md index 89ad4f7b990..a2f83f2bbc6 100644 --- a/docs/reference/go-gno-compatibility.md +++ b/docs/reference/go-gno-compatibility.md @@ -34,7 +34,7 @@ id: go-gno-compatibility Generics are currently not implemented. -Note that Gno does not support shadowing of built-in types. +Note that Gno does not support shadowing of built-in types. While the following built-in typecasting assignment would work in Go, this is not supported in Gno. ```go @@ -205,7 +205,7 @@ Legend: | math/big | `tbd` | | math/bits | `full` | | math/cmplx | `tbd` | -| math/rand | `todo` | +| math/rand | `full`[^9] | | mime | `tbd` | | mime/multipart | `tbd` | | mime/quotedprintable | `tbd` | @@ -291,6 +291,7 @@ Legend: determinism. Concurrent functionality (such as `time.Ticker`) is not implemented. [^8]: `crypto/ed25519` is currently only implemented for `Verify`, which should still cover a majority of use cases. A full implementation is welcome. +[^9]: `math/rand` in Gno ports over Go's `math/rand/v2`. ## Tooling (`gno` binary) diff --git a/examples/gno.land/p/demo/rand/gno.mod b/examples/gno.land/p/demo/rand/gno.mod deleted file mode 100644 index 098af152648..00000000000 --- a/examples/gno.land/p/demo/rand/gno.mod +++ /dev/null @@ -1,3 +0,0 @@ -// Draft - -module gno.land/p/demo/rand diff --git a/examples/gno.land/p/demo/rand/rand.gno b/examples/gno.land/p/demo/rand/rand.gno deleted file mode 100644 index 2fa16d627be..00000000000 --- a/examples/gno.land/p/demo/rand/rand.gno +++ /dev/null @@ -1,139 +0,0 @@ -package rand - -// Disclaimer: this package is unsafe and won't prevent others to -// guess values in advance. -// -// the goal of this package is to implement a random library that -// is fully deterministic for validators while being hard to guess. -// -// We use the Bernstein's hash djb2 to be CPU-cycle efficient. - -import ( - "math/rand" - "std" - "time" -) - -type Instance struct { - seed int64 -} - -func New() *Instance { - r := Instance{seed: 5381} - r.addEntropy() - return &r -} - -func FromSeed(seed int64) *Instance { - r := Instance{seed: seed} - r.addEntropy() - return &r -} - -func (i *Instance) Seed() int64 { - return i.seed -} - -func (i *Instance) djb2String(input string) { - for _, c := range input { - i.djb2Int64(int64(c)) - } -} - -// super fast random algorithm. -// http://www.cse.yorku.ca/~oz/hash.html -func (i *Instance) djb2Int64(input int64) { - i.seed = (i.seed << 5) + i.seed + input -} - -// AddEntropy uses various runtime variables to add entropy to the existing seed. -func (i *Instance) addEntropy() { - // FIXME: reapply the 5381 initial value? - - // inherit previous entropy - // nothing to do - - // handle callers - { - caller1 := std.GetCallerAt(1).String() - i.djb2String(caller1) - caller2 := std.GetCallerAt(2).String() - i.djb2String(caller2) - } - - // height - { - height := std.GetHeight() - i.djb2Int64(height) - } - - // time - { - secs := time.Now().Second() - i.djb2Int64(int64(secs)) - nsecs := time.Now().Nanosecond() - i.djb2Int64(int64(nsecs)) - } - - // FIXME: compute other hard-to-guess but deterministic variables, like real gas? -} - -func (i *Instance) Float32() float32 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Float32() -} - -func (i *Instance) Float64() float64 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Float64() -} - -func (i *Instance) Int() int { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Int() -} - -func (i *Instance) Intn(n int) int { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Intn(n) -} - -func (i *Instance) Int63() int64 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Int63() -} - -func (i *Instance) Int63n(n int64) int64 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Int63n(n) -} - -func (i *Instance) Int31() int32 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Int31() -} - -func (i *Instance) Int31n(n int32) int32 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Int31n(n) -} - -func (i *Instance) Uint32() uint32 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Uint32() -} - -func (i *Instance) Uint64() uint64 { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Uint64() -} - -func (i *Instance) Read(p []byte) (n int, err error) { - i.addEntropy() - return rand.New(rand.NewSource(i.seed)).Read(p) -} - -func (i *Instance) Shuffle(n int, swap func(i, j int)) { - i.addEntropy() - rand.New(rand.NewSource(i.seed)).Shuffle(n, swap) -} diff --git a/examples/gno.land/p/demo/rand/rand0_filetest.gno b/examples/gno.land/p/demo/rand/rand0_filetest.gno deleted file mode 100644 index 446e04b696d..00000000000 --- a/examples/gno.land/p/demo/rand/rand0_filetest.gno +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "std" - - "gno.land/p/demo/rand" -) - -func main() { - // initial - println("---") - r := rand.New() - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - - // should be the same - println("---") - r = rand.New() - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - - std.TestSkipHeights(1) - println("---") - r = rand.New() - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) - println(r.Intn(1000)) -} - -// Output: -// --- -// 777 -// 257 -// 74 -// 177 -// 802 -// --- -// 777 -// 257 -// 74 -// 177 -// 802 -// --- -// 269 -// 233 -// 591 -// 936 -// 908 diff --git a/examples/gno.land/p/demo/rand/rand_test.gno b/examples/gno.land/p/demo/rand/rand_test.gno deleted file mode 100644 index 2651f0af089..00000000000 --- a/examples/gno.land/p/demo/rand/rand_test.gno +++ /dev/null @@ -1,49 +0,0 @@ -package rand - -import ( - "fmt" - "std" - "strings" - "testing" - - "gno.land/p/demo/rand" -) - -func TestInstance(t *testing.T) { - instance := rand.New() - if instance == nil { - t.Errorf("instance should not be nil") - } -} - -func TestIntn(t *testing.T) { - baseRand := rand.New() - baseResult := computeIntn(t, baseRand) - - sameHeightRand := rand.New() - sameHeightResult := computeIntn(t, sameHeightRand) - - if baseResult != sameHeightResult { - t.Errorf("should have the same result: new=%s, base=%s", sameHeightResult, baseResult) - } - - std.TestSkipHeights(1) - differentHeightRand := rand.New() - differentHeightResult := computeIntn(t, differentHeightRand) - - if baseResult == differentHeightResult { - t.Errorf("should have different result: new=%s, base=%s", differentHeightResult, baseResult) - } -} - -func computeIntn(t *testing.T, r *rand.Instance) string { - t.Helper() - - arr := []string{} - for i := 0; i < 10; i++ { - arr = append(arr, fmt.Sprintf("%d", r.Intn(1000))) - } - - out := strings.Join(arr, ",") - return out -} diff --git a/examples/gno.land/r/demo/art/gnoface/gno.mod b/examples/gno.land/r/demo/art/gnoface/gno.mod index 6276629cba2..f2d3ddebadc 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,8 +1,3 @@ -// Draft - module gno.land/r/demo/art/gnoface -require ( - gno.land/p/demo/rand v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/r/demo/art/gnoface/gnoface.gno b/examples/gno.land/r/demo/art/gnoface/gnoface.gno index 95493b52bf5..9e85c5c7387 100644 --- a/examples/gno.land/r/demo/art/gnoface/gnoface.gno +++ b/examples/gno.land/r/demo/art/gnoface/gnoface.gno @@ -1,16 +1,16 @@ package gnoface import ( + "math/rand" "std" "strconv" "strings" - "gno.land/p/demo/rand" "gno.land/p/demo/ufmt" ) func Render(path string) string { - seed := std.GetHeight() + seed := uint64(std.GetHeight()) path = strings.TrimSpace(path) if path != "" { @@ -18,7 +18,7 @@ func Render(path string) string { if err != nil { panic(err) } - seed = int64(s) + seed = uint64(s) } output := ufmt.Sprintf("Gnoface #%d\n", seed) @@ -26,7 +26,7 @@ func Render(path string) string { return output } -func Draw(seed int64) string { +func Draw(seed uint64) string { var ( hairs = []string{ " s", @@ -102,7 +102,7 @@ func Draw(seed int64) string { } ) - r := rand.FromSeed(seed) + r := rand.New(rand.NewPCG(seed, 0xdeadbeef)) return pick(r, hairs) + "\n" + pick(r, headtop) + "\n" + @@ -117,8 +117,8 @@ func Draw(seed int64) string { pick(r, headbottom) + "\n" } -func pick(r *rand.Instance, slice []string) string { - return slice[r.Intn(len(slice))] +func pick(r *rand.Rand, slice []string) string { + return slice[r.IntN(len(slice))] } // based on https://github.com/moul/pipotron/blob/master/dict/ascii-face.yml diff --git a/examples/gno.land/r/demo/art/gnoface/gnoface_test.gno b/examples/gno.land/r/demo/art/gnoface/gnoface_test.gno index 630cce85c55..e82bd819483 100644 --- a/examples/gno.land/r/demo/art/gnoface/gnoface_test.gno +++ b/examples/gno.land/r/demo/art/gnoface/gnoface_test.gno @@ -8,21 +8,21 @@ import ( func TestDraw(t *testing.T) { cases := []struct { - seed int64 + seed uint64 expected string }{ { seed: 42, expected: ` ||||||| - ////////\ + ||||||||| | | - | ~ . | -)| X X |. + | . ~ | +)| v v |O | | - | C | + | L | | | - | __/ | + | ___ | | | \~~~~~~~/ `[1:], @@ -30,31 +30,31 @@ func TestDraw(t *testing.T) { { seed: 1337, expected: ` - s - /|||||||\ + ....... + ||||||||| | | - | . * | -o| ~ ~ |. + | . _ | +D| x X |O | | - | O | + | ~ | | | - | __/ | + | ~~~ | | | - \_______/ + \~~~~~~~/ `[1:], }, { seed: 123456789, expected: ` - s - /~~~~~~~\ + ....... + ////////\ | | - | ~ . | -<| ~ ~ |< + | ~ * | +|| x X |o | | | V | | | - | \_/ | + | . | | | \-------/ `[1:], @@ -80,14 +80,14 @@ func TestRender(t *testing.T) { path: "42", expected: "Gnoface #42\n```" + ` ||||||| - ////////\ + ||||||||| | | - | ~ . | -)| X X |. + | . ~ | +)| v v |O | | - | C | + | L | | | - | __/ | + | ___ | | | \~~~~~~~/ ` + "```\n", @@ -95,31 +95,31 @@ func TestRender(t *testing.T) { { path: "1337", expected: "Gnoface #1337\n```" + ` - s - /|||||||\ + ....... + ||||||||| | | - | . * | -o| ~ ~ |. + | . _ | +D| x X |O | | - | O | + | ~ | | | - | __/ | + | ~~~ | | | - \_______/ + \~~~~~~~/ ` + "```\n", }, { path: "123456789", expected: "Gnoface #123456789\n```" + ` - s - /~~~~~~~\ + ....... + ////////\ | | - | ~ . | -<| ~ ~ |< + | ~ * | +|| x X |o | | | V | | | - | \_/ | + | . | | | \-------/ ` + "```\n", diff --git a/examples/gno.land/r/x/manfred_outfmt/gno.mod b/examples/gno.land/r/x/manfred_outfmt/gno.mod index 9804aecc7f1..7044f0f72b3 100644 --- a/examples/gno.land/r/x/manfred_outfmt/gno.mod +++ b/examples/gno.land/r/x/manfred_outfmt/gno.mod @@ -2,7 +2,4 @@ module gno.land/r/x/manfred_outfmt -require ( - gno.land/p/demo/rand v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/r/x/manfred_outfmt/outfmt.gno b/examples/gno.land/r/x/manfred_outfmt/outfmt.gno index 5468a65c06f..01981024189 100644 --- a/examples/gno.land/r/x/manfred_outfmt/outfmt.gno +++ b/examples/gno.land/r/x/manfred_outfmt/outfmt.gno @@ -2,9 +2,9 @@ package outfmt import ( "encoding/json" + "math/rand" "strings" - "gno.land/p/demo/rand" "gno.land/p/demo/ufmt" ) @@ -27,22 +27,21 @@ func (res *Result) String() string { return output } -var rSeed int64 +var rSeed = rand.NewPCG(0, 0) func genResult() Result { - // init rand - r := rand.FromSeed(rSeed) - defer func() { rSeed = r.Seed() }() + r := rand.New(rSeed) + // init rand res := Result{ Text: "Hello Gnomes!", - Number: r.Intn(1000), + Number: r.IntN(1000), } - length := r.Intn(8) + 2 + length := r.IntN(8) + 2 res.Numbers = make([]int, length) for i := 0; i < length; i++ { - res.Numbers[i] = r.Intn(100) + res.Numbers[i] = r.IntN(100) } return res diff --git a/examples/gno.land/r/x/manfred_outfmt/outfmt_test.gno b/examples/gno.land/r/x/manfred_outfmt/outfmt_test.gno index c0c14139d52..7bcd9e48f80 100644 --- a/examples/gno.land/r/x/manfred_outfmt/outfmt_test.gno +++ b/examples/gno.land/r/x/manfred_outfmt/outfmt_test.gno @@ -15,7 +15,7 @@ func TestRender(t *testing.T) { * [?fmt=jsonp](/r/x/manfred_outfmt:?fmt=jsonp) ` if got != expected { - t.Fatalf("expected %q, got %q.", expected, got) + t.Errorf("expected %q, got %q.", expected, got) } } @@ -23,11 +23,11 @@ func TestRender(t *testing.T) { { got := outfmt.Render("?fmt=stringer") expected := `Text: Hello Gnomes! -Number: 957 -Numbers: 3 54 32 88 +Number: 222 +Numbers: 34 44 39 7 72 48 74 ` if got != expected { - t.Fatalf("expected %q, got %q.", expected, got) + t.Errorf("expected %q, got %q.", expected, got) } } @@ -35,29 +35,29 @@ Numbers: 3 54 32 88 { got := outfmt.Render("?fmt=stringer") expected := `Text: Hello Gnomes! -Number: 141 -Numbers: 98 27 +Number: 898 +Numbers: 24 25 2 ` if got != expected { - t.Fatalf("expected %q, got %q.", expected, got) + t.Errorf("expected %q, got %q.", expected, got) } } // json { got := outfmt.Render("?fmt=json") - expected := `{"Number":801,"Text":"Hello Gnomes!","Numbers":[5,78,51,78,91,41]}` + expected := `{"Number":746,"Text":"Hello Gnomes!","Numbers":[57,82,16,14,28,32]}` if got != expected { - t.Fatalf("expected %q, got %q.", expected, got) + t.Errorf("expected %q, got %q.", expected, got) } } // jsonp { got := outfmt.Render("?fmt=jsonp") - expected := `callback({"Number":63,"Text":"Hello Gnomes!","Numbers":[2,66,50,73,81]})` + expected := `callback({"Number":795,"Text":"Hello Gnomes!","Numbers":[29,51,88,61,93,21,2,66,79]})` if got != expected { - t.Fatalf("expected %q, got %q.", expected, got) + t.Errorf("expected %q, got %q.", expected, got) } } } diff --git a/gnovm/stdlibs/bytes/buffer_test.gno b/gnovm/stdlibs/bytes/buffer_test.gno index a8837494224..601901955cd 100644 --- a/gnovm/stdlibs/bytes/buffer_test.gno +++ b/gnovm/stdlibs/bytes/buffer_test.gno @@ -216,14 +216,14 @@ func TestMixedReadsAndWrites(t *testing.T) { var buf bytes.Buffer s := "" for i := 0; i < 50; i++ { - wlen := rand.Intn(len(testString)) + wlen := rand.IntN(len(testString)) if i%2 == 0 { s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testString[0:wlen]) } else { s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testBytes[0:wlen]) } - rlen := rand.Intn(len(testString)) + rlen := rand.IntN(len(testString)) fub := make([]byte, rlen) n, _ := buf.Read(fub) s = s[n:] diff --git a/gnovm/stdlibs/bytes/bytes_test.gno b/gnovm/stdlibs/bytes/bytes_test.gno index c7762f2f67b..927f89c5559 100644 --- a/gnovm/stdlibs/bytes/bytes_test.gno +++ b/gnovm/stdlibs/bytes/bytes_test.gno @@ -1707,7 +1707,7 @@ var makeFieldsInput = func() []byte { x := make([]byte, 1<<20) // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. for i := range x { - switch rand.Intn(10) { + switch rand.IntN(10) { case 0: x[i] = ' ' case 1: @@ -1729,7 +1729,7 @@ var makeFieldsInputASCII = func() []byte { x := make([]byte, 1<<20) // Input is ~10% space, rest ASCII non-space. for i := range x { - if rand.Intn(10) == 0 { + if rand.IntN(10) == 0 { x[i] = ' ' } else { x[i] = 'x' @@ -1827,7 +1827,7 @@ func makeBenchInputHard() []byte { } x := make([]byte, 0, 1<<20) for { - i := rand.Intn(len(tokens)) + i := rand.IntN(len(tokens)) if len(x)+len(tokens[i]) >= 1<<20 { break } diff --git a/gnovm/stdlibs/math/rand/auto_test.gno b/gnovm/stdlibs/math/rand/auto_test.gno new file mode 100644 index 00000000000..5945039ae18 --- /dev/null +++ b/gnovm/stdlibs/math/rand/auto_test.gno @@ -0,0 +1,39 @@ +// 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 + +import ( + "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 seeded result. + var out []int64 + for i := 0; i < 10; i++ { + out = append(out, Int64()) + } + + // Look for out in seeded 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. + r := New(NewPCG(1, 0)) + found := 0 + for i := 0; i < 1000; i++ { + x := r.Int64() + if x == out[found] { + found++ + if found == len(out) { + t.Fatalf("found unseeded output in Seed(1) output") + } + } + } +} diff --git a/gnovm/stdlibs/math/rand/example_test.gno b/gnovm/stdlibs/math/rand/example_test.gno new file mode 100644 index 00000000000..0cffec1f3d1 --- /dev/null +++ b/gnovm/stdlibs/math/rand/example_test.gno @@ -0,0 +1,131 @@ +// 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" + "os" + "strings" + "time" +) + +// 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 Uint64(), Uint64(). + // Using a fixed seed will produce the same output on every run. + r := rand.New(rand.NewPCG(1, 2)) + + // XXX: Go uses tabwriter; we don't have it, so use stdout directly. + show := func(name string, v1, v2, v3 interface{}) { + fmt.Printf("%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()) + + // Int32, Int64, and Uint32 generate values of the given width. + // The Int method (not shown) is like either Int32 or Int64 + // depending on the size of 'int'. + show("Int32", r.Int32(), r.Int32(), r.Int32()) + show("Int64", r.Int64(), r.Int64(), r.Int64()) + show("Uint32", r.Uint32(), r.Uint32(), r.Uint32()) + + // IntN, Int32N, and Int64N 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("Int32N(10)", r.Int32N(10), r.Int32N(10), r.Int32N(10)) + show("Int64N(10)", r.Int64N(10), r.Int64N(10), r.Int64N(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.95955694 0.8076733 0.8135684 + // Float64 0.4297927436037299 0.797802349388613 0.3883664855410056 + // ExpFloat64 0.43463410545541104 0.5513632046504593 0.7426404617374481 + // NormFloat64 -0.9303318111676635 -0.04750789419852852 0.22248301107582735 + // Int32 2020777787 260808523 851126509 + // Int64 5231057920893523323 4257872588489500903 158397175702351138 + // Uint32 314478343 1418758728 208955345 + // IntN(10) 6 2 0 + // Int32N(10) 3 7 7 + // Int64N(10) 8 9 4 + // Perm [0 3 1 4 2] [4 1 2 0 3] [4 3 2 0 1] +} + +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)) +} diff --git a/gnovm/stdlibs/math/rand/exp.gno b/gnovm/stdlibs/math/rand/exp.gno new file mode 100644 index 00000000000..ed7f7277bc5 --- /dev/null +++ b/gnovm/stdlibs/math/rand/exp.gno @@ -0,0 +1,222 @@ +// Copyright 2009 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 + +import ( + "math" +) + +/* + * Exponential distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * https://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + re = 7.69711747013104972 +) + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1). +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func (r *Rand) ExpFloat64() float64 { + for { + u := r.Uint64() + j := uint32(u) + i := uint8(u >> 32) + x := float64(j) * float64(we[i]) + if j < ke[i] { + return x + } + if i == 0 { + return re - math.Log(r.Float64()) + } + if fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) { + return x + } + } +} + +var ke = [256]uint32{ + 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990, + 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8, + 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78, + 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651, + 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca, + 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8, + 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea, + 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba, + 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed, + 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662, + 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3, + 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace, + 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6, + 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7, + 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415, + 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4, + 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36, + 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46, + 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac, + 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245, + 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52, + 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06, + 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0, + 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9, + 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76, + 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516, + 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289, + 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed, + 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb, + 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e, + 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a, + 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1, + 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b, + 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621, + 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d, + 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3, + 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73, + 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88, + 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a, + 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb, + 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176, + 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be, + 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192, + 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed, + 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936, + 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b, + 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4, + 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1, + 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482, + 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023, + 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d, + 0xe6da6ecf, +} +var we = [256]float32{ + 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11, + 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11, + 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11, + 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11, + 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10, + 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10, + 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10, + 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10, + 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10, + 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10, + 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10, + 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10, + 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10, + 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10, + 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10, + 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10, + 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10, + 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10, + 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10, + 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10, + 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10, + 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10, + 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10, + 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10, + 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10, + 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10, + 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10, + 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10, + 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10, + 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10, + 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10, + 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10, + 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10, + 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10, + 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10, + 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10, + 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10, + 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10, + 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10, + 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10, + 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10, + 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10, + 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10, + 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10, + 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10, + 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10, + 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10, + 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10, + 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10, + 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10, + 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10, + 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10, + 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10, + 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10, + 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10, + 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10, + 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10, + 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10, + 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10, + 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09, + 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09, + 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09, + 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09, + 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09, +} +var fe = [256]float32{ + 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933, + 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686, + 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665, + 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967, + 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896, + 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092, + 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386, + 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495, + 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752, + 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325, + 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955, + 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694, + 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218, + 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763, + 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044, + 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796, + 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408, + 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928, + 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393, + 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625, + 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107, + 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878, + 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438, + 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682, + 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852, + 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479, + 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354, + 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494, + 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119, + 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624, + 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574, + 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672, + 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763, + 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816, + 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919, + 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274, + 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195, + 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106, + 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434, + 0.062193416, 0.060783047, 0.059384305, 0.057997175, + 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236, + 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623, + 0.043502413, 0.042254124, 0.041017443, 0.039792392, + 0.038578995, 0.037377283, 0.036187284, 0.035009038, + 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566, + 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421, + 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867, + 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392, + 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414, + 0.008780315, 0.007963077, 0.0071633533, 0.006381906, + 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648, + 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693, + 0.00045413437, +} diff --git a/gnovm/stdlibs/math/rand/normal.gno b/gnovm/stdlibs/math/rand/normal.gno new file mode 100644 index 00000000000..ea1ae409b4c --- /dev/null +++ b/gnovm/stdlibs/math/rand/normal.gno @@ -0,0 +1,157 @@ +// Copyright 2009 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 + +import ( + "math" +) + +/* + * Normal distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + rn = 3.442619855899 +) + +func absInt32(i int32) uint32 { + if i < 0 { + return uint32(-i) + } + return uint32(i) +} + +// NormFloat64 returns a normally distributed float64 in +// the range -math.MaxFloat64 through +math.MaxFloat64 inclusive, +// with standard normal distribution (mean = 0, stddev = 1). +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func (r *Rand) NormFloat64() float64 { + for { + u := r.Uint64() + j := int32(u) // Possibly negative + i := u >> 32 & 0x7F + x := float64(j) * float64(wn[i]) + if absInt32(j) < kn[i] { + // This case should be hit better than 99% of the time. + return x + } + + if i == 0 { + // This extra work is only required for the base strip. + for { + x = -math.Log(r.Float64()) * (1.0 / rn) + y := -math.Log(r.Float64()) + if y+y >= x*x { + break + } + } + if j > 0 { + return rn + x + } + return -rn - x + } + if fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) { + return x + } + } +} + +var kn = [128]uint32{ + 0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2, + 0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d, + 0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7, + 0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883, + 0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30, + 0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa, + 0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d, + 0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18, + 0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924, + 0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a, + 0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4, + 0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62, + 0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e, + 0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473, + 0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd, + 0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568, + 0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08, + 0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc, + 0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94, + 0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb, + 0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075, + 0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba, + 0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded, + 0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72, + 0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a, + 0x7ba90bdc, 0x7a722176, 0x77d664e5, +} +var wn = [128]float32{ + 1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10, + 2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10, + 2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10, + 3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10, + 3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10, + 4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10, + 4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10, + 4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10, + 5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10, + 5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10, + 5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10, + 5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10, + 6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10, + 6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10, + 6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10, + 6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10, + 7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10, + 7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10, + 7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10, + 7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10, + 8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10, + 8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10, + 8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10, + 9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10, + 9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10, + 9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09, + 1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09, + 1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09, + 1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09, + 1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09, + 1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09, + 1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09, +} +var fn = [128]float32{ + 1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303, + 0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177, + 0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569, + 0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277, + 0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434, + 0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903, + 0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055, + 0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766, + 0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872, + 0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642, + 0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446, + 0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685, + 0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484, + 0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807, + 0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239, + 0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877, + 0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499, + 0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037, + 0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265, + 0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602, + 0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467, + 0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058, + 0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863, + 0.040742867, 0.03688439, 0.033087887, 0.029356318, + 0.025693292, 0.022103304, 0.018592102, 0.015167298, + 0.011839478, 0.008624485, 0.005548995, 0.0026696292, +} diff --git a/gnovm/stdlibs/math/rand/pcg.gno b/gnovm/stdlibs/math/rand/pcg.gno new file mode 100644 index 00000000000..77708d799e2 --- /dev/null +++ b/gnovm/stdlibs/math/rand/pcg.gno @@ -0,0 +1,121 @@ +// 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 + +import ( + "errors" + "math/bits" +) + +// https://numpy.org/devdocs/reference/random/upgrading-pcg64.html +// https://github.com/imneme/pcg-cpp/commit/871d0494ee9c9a7b7c43f753e3d8ca47c26f8005 + +// A PCG is a PCG generator with 128 bits of internal state. +// A zero PCG is equivalent to NewPCG(0, 0). +type PCG struct { + hi uint64 + lo uint64 +} + +// NewPCG returns a new PCG seeded with the given values. +func NewPCG(seed1, seed2 uint64) *PCG { + return &PCG{seed1, seed2} +} + +// Seed resets the PCG to behave the same way as NewPCG(seed1, seed2). +func (p *PCG) Seed(seed1, seed2 uint64) { + p.hi = seed1 + p.lo = seed2 +} + +// binary.bigEndian.Uint64, copied to avoid dependency +func beUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +// binary.bigEndian.PutUint64, copied to avoid dependency +func bePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (p *PCG) MarshalBinary() ([]byte, error) { + b := make([]byte, 20) + copy(b, "pcg:") + bePutUint64(b[4:], p.hi) + bePutUint64(b[4+8:], p.lo) + return b, nil +} + +var errUnmarshalPCG = errors.New("invalid PCG encoding") + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (p *PCG) UnmarshalBinary(data []byte) error { + if len(data) != 20 || string(data[:4]) != "pcg:" { + return errUnmarshalPCG + } + p.hi = beUint64(data[4:]) + p.lo = beUint64(data[4+8:]) + return nil +} + +func (p *PCG) next() (hi, lo uint64) { + // https://github.com/imneme/pcg-cpp/blob/428802d1a5/include/pcg_random.hpp#L161 + // + // Numpy's PCG multiplies by the 64-bit value cheapMul + // instead of the 128-bit value used here and in the official PCG code. + // This does not seem worthwhile, at least for Go: not having any high + // bits in the multiplier reduces the effect of low bits on the highest bits, + // and it only saves 1 multiply out of 3. + // (On 32-bit systems, it saves 1 out of 6, since Mul64 is doing 4.) + const ( + mulHi = 2549297995355413924 + mulLo = 4865540595714422341 + incHi = 6364136223846793005 + incLo = 1442695040888963407 + ) + + // state = state * mul + inc + hi, lo = bits.Mul64(p.lo, mulLo) + hi += p.hi*mulLo + p.lo*mulHi + lo, c := bits.Add64(lo, incLo, 0) + hi, _ = bits.Add64(hi, incHi, c) + p.lo = lo + p.hi = hi + return hi, lo +} + +// Uint64 return a uniformly-distributed random uint64 value. +func (p *PCG) Uint64() uint64 { + hi, lo := p.next() + + // XSL-RR would be + // hi, lo := p.next() + // return bits.RotateLeft64(lo^hi, -int(hi>>58)) + // but Numpy uses DXSM and O'Neill suggests doing the same. + // See https://github.com/golang/go/issues/21835#issuecomment-739065688 + // and following comments. + + // DXSM "double xorshift multiply" + // https://github.com/imneme/pcg-cpp/blob/428802d1a5/include/pcg_random.hpp#L1015 + + // https://github.com/imneme/pcg-cpp/blob/428802d1a5/include/pcg_random.hpp#L176 + const cheapMul = 0xda942042e4dd58b5 + hi ^= hi >> 32 + hi *= cheapMul + hi ^= hi >> 48 + hi *= (lo | 1) + return hi +} diff --git a/gnovm/stdlibs/math/rand/pcg_test.gno b/gnovm/stdlibs/math/rand/pcg_test.gno new file mode 100644 index 00000000000..843506d7234 --- /dev/null +++ b/gnovm/stdlibs/math/rand/pcg_test.gno @@ -0,0 +1,78 @@ +// 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 + +import ( + "testing" +) + +func BenchmarkPCG_DXSM(b *testing.B) { + var p PCG + var t uint64 + for n := b.N; n > 0; n-- { + t += p.Uint64() + } + Sink = t +} + +func TestPCGMarshal(t *testing.T) { + var p PCG + const ( + seed1 = 0x123456789abcdef0 + seed2 = 0xfedcba9876543210 + want = "pcg:\x12\x34\x56\x78\x9a\xbc\xde\xf0\xfe\xdc\xba\x98\x76\x54\x32\x10" + ) + p.Seed(seed1, seed2) + data, err := p.MarshalBinary() + if string(data) != want || err != nil { + t.Errorf("MarshalBinary() = %q, %v, want %q, nil", data, err, want) + } + + q := PCG{} + if err := q.UnmarshalBinary([]byte(want)); err != nil { + t.Fatalf("UnmarshalBinary(): %v", err) + } + if q != p { + t.Fatalf("after round trip, q = %#x, but p = %#x", q, p) + } + + qu := q.Uint64() + pu := p.Uint64() + if qu != pu { + t.Errorf("after round trip, q.Uint64() = %#x, but p.Uint64() = %#x", qu, pu) + } +} + +func TestPCG(t *testing.T) { + p := NewPCG(1, 2) + want := []uint64{ + 0xc4f5a58656eef510, + 0x9dcec3ad077dec6c, + 0xc8d04605312f8088, + 0xcbedc0dcb63ac19a, + 0x3bf98798cae97950, + 0xa8c6d7f8d485abc, + 0x7ffa3780429cd279, + 0x730ad2626b1c2f8e, + 0x21ff2330f4a0ad99, + 0x2f0901a1947094b0, + 0xa9735a3cfbe36cef, + 0x71ddb0a01a12c84a, + 0xf0e53e77a78453bb, + 0x1f173e9663be1e9d, + 0x657651da3ac4115e, + 0xc8987376b65a157b, + 0xbb17008f5fca28e7, + 0x8232bd645f29ed22, + 0x12be8f07ad14c539, + 0x54908a48e8e4736e, + } + + for i, x := range want { + if u := p.Uint64(); u != x { + t.Errorf("PCG #%d = %#x, want %#x", i, u, x) + } + } +} diff --git a/gnovm/stdlibs/math/rand/rand.gno b/gnovm/stdlibs/math/rand/rand.gno new file mode 100644 index 00000000000..250ea7c5a05 --- /dev/null +++ b/gnovm/stdlibs/math/rand/rand.gno @@ -0,0 +1,340 @@ +// Copyright 2009 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 implements pseudo-random number generators suitable for tasks +// such as simulation, but it should not be used for security-sensitive work. +// +// IMPORTANT NOTE: This does not equate to Go's math/rand; instead, it uses +// the implementation in math/rand/v2, which improves on its algorithm and +// usability. +// +// Random numbers are generated by a [Source], usually wrapped in a [Rand]. +// Both types should be used by a single goroutine at a time: sharing among +// multiple goroutines requires some kind of synchronization. +// +// Top-level functions, such as [Float64] and [Int], +// are safe for concurrent use by multiple goroutines. +// +// This package's outputs might be easily predictable regardless of how it's +// seeded. +package rand + +import ( + "math/bits" +) + +// A Source is a source of uniformly-distributed +// pseudo-random uint64 values in the range [0, 1<<64). +// +// A Source is not safe for concurrent use by multiple goroutines. +type Source interface { + Uint64() uint64 +} + +// A Rand is a source of random numbers. +type Rand struct { + src Source +} + +// New returns a new Rand that uses random values from src +// to generate other random values. +func New(src Source) *Rand { + return &Rand{src: src} +} + +// Int64 returns a non-negative pseudo-random 63-bit integer as an int64. +func (r *Rand) Int64() int64 { return int64(r.src.Uint64() &^ (1 << 63)) } + +// Uint32 returns a pseudo-random 32-bit value as a uint32. +func (r *Rand) Uint32() uint32 { return uint32(r.src.Uint64() >> 32) } + +// Uint64 returns a pseudo-random 64-bit value as a uint64. +func (r *Rand) Uint64() uint64 { return r.src.Uint64() } + +// Int32 returns a non-negative pseudo-random 31-bit integer as an int32. +func (r *Rand) Int32() int32 { return int32(r.src.Uint64() >> 33) } + +// Int returns a non-negative pseudo-random int. +func (r *Rand) Int() int { return int(uint(r.src.Uint64()) << 1 >> 1) } + +// Int64N returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) Int64N(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int64N") + } + return int64(r.uint64n(uint64(n))) +} + +// Uint64N returns, as a uint64, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n == 0. +func (r *Rand) Uint64N(n uint64) uint64 { + if n == 0 { + panic("invalid argument to Uint64N") + } + return r.uint64n(n) +} + +// uint64n is the no-bounds-checks version of Uint64N. +func (r *Rand) uint64n(n uint64) uint64 { + if is32bit && uint64(uint32(n)) == n { + return uint64(r.uint32n(uint32(n))) + } + if n&(n-1) == 0 { // n is power of two, can mask + return r.Uint64() & (n - 1) + } + + // Suppose we have a uint64 x uniform in the range [0,2⁶⁴) + // and want to reduce it to the range [0,n) preserving exact uniformity. + // We can simulate a scaling arbitrary precision x * (n/2⁶⁴) by + // the high bits of a double-width multiply of x*n, meaning (x*n)/2⁶⁴. + // Since there are 2⁶⁴ possible inputs x and only n possible outputs, + // the output is necessarily biased if n does not divide 2⁶⁴. + // In general (x*n)/2⁶⁴ = k for x*n in [k*2⁶⁴,(k+1)*2⁶⁴). + // There are either floor(2⁶⁴/n) or ceil(2⁶⁴/n) possible products + // in that range, depending on k. + // But suppose we reject the sample and try again when + // x*n is in [k*2⁶⁴, k*2⁶⁴+(2⁶⁴%n)), meaning rejecting fewer than n possible + // outcomes out of the 2⁶⁴. + // Now there are exactly floor(2⁶⁴/n) possible ways to produce + // each output value k, so we've restored uniformity. + // To get valid uint64 math, 2⁶⁴ % n = (2⁶⁴ - n) % n = -n % n, + // so the direct implementation of this algorithm would be: + // + // hi, lo := bits.Mul64(r.Uint64(), n) + // thresh := -n % n + // for lo < thresh { + // hi, lo = bits.Mul64(r.Uint64(), n) + // } + // + // That still leaves an expensive 64-bit division that we would rather avoid. + // We know that thresh < n, and n is usually much less than 2⁶⁴, so we can + // avoid the last four lines unless lo < n. + // + // See also: + // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction + // https://lemire.me/blog/2016/06/30/fast-random-shuffling + hi, lo := bits.Mul64(r.Uint64(), n) + if lo < n { + thresh := -n % n + for lo < thresh { + hi, lo = bits.Mul64(r.Uint64(), n) + } + } + return hi +} + +// uint32n is an identical computation to uint64n +// but optimized for 32-bit systems. +func (r *Rand) uint32n(n uint32) uint32 { + if n&(n-1) == 0 { // n is power of two, can mask + return uint32(r.Uint64()) & (n - 1) + } + // On 64-bit systems we still use the uint64 code below because + // the probability of a random uint64 lo being < a uint32 n is near zero, + // meaning the unbiasing loop almost never runs. + // On 32-bit systems, here we need to implement that same logic in 32-bit math, + // both to preserve the exact output sequence observed on 64-bit machines + // and to preserve the optimization that the unbiasing loop almost never runs. + // + // We want to compute + // hi, lo := bits.Mul64(r.Uint64(), n) + // In terms of 32-bit halves, this is: + // x1:x0 := r.Uint64() + // 0:hi, lo1:lo0 := bits.Mul64(x1:x0, 0:n) + // Writing out the multiplication in terms of bits.Mul32 allows + // using direct hardware instructions and avoiding + // the computations involving these zeros. + x := r.Uint64() + lo1a, lo0 := bits.Mul32(uint32(x), n) + hi, lo1b := bits.Mul32(uint32(x>>32), n) + lo1, c := bits.Add32(lo1a, lo1b, 0) + hi += c + if lo1 == 0 && lo0 < uint32(n) { + n64 := uint64(n) + thresh := uint32(-n64 % n64) + for lo1 == 0 && lo0 < thresh { + x := r.Uint64() + lo1a, lo0 = bits.Mul32(uint32(x), n) + hi, lo1b = bits.Mul32(uint32(x>>32), n) + lo1, c = bits.Add32(lo1a, lo1b, 0) + hi += c + } + } + return hi +} + +// Int32N returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) Int32N(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int32N") + } + return int32(r.uint64n(uint64(n))) +} + +// Uint32N returns, as a uint32, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n == 0. +func (r *Rand) Uint32N(n uint32) uint32 { + if n == 0 { + panic("invalid argument to Uint32N") + } + return uint32(r.uint64n(uint64(n))) +} + +const is32bit = ^uint(0)>>32 == 0 + +// IntN returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) IntN(n int) int { + if n <= 0 { + panic("invalid argument to IntN") + } + return int(r.uint64n(uint64(n))) +} + +// UintN returns, as a uint, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n == 0. +func (r *Rand) UintN(n uint) uint { + if n == 0 { + panic("invalid argument to UintN") + } + return uint(r.uint64n(uint64(n))) +} + +// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0). +func (r *Rand) Float64() float64 { + // There are exactly 1<<53 float64s in [0,1). Use Intn(1<<53) / (1<<53). + return float64(r.Uint64()<<11>>11) / (1 << 53) +} + +// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0). +func (r *Rand) Float32() float32 { + // There are exactly 1<<24 float32s in [0,1). Use Intn(1<<24) / (1<<24). + return float32(r.Uint32()<<8>>8) / (1 << 24) +} + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers +// in the half-open interval [0,n). +func (r *Rand) Perm(n int) []int { + p := make([]int, n) + for i := range p { + p[i] = i + } + r.Shuffle(len(p), func(i, j int) { p[i], p[j] = p[j], p[i] }) + return p +} + +// Shuffle pseudo-randomizes the order of elements. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func (r *Rand) Shuffle(n int, swap func(i, j int)) { + if n < 0 { + panic("invalid argument to Shuffle") + } + + // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + // Shuffle really ought not be called with n that doesn't fit in 32 bits. + // Not only will it take a very long time, but with 2³¹! possible permutations, + // there's no way that any PRNG can have a big enough internal state to + // generate even a minuscule percentage of the possible permutations. + // Nevertheless, the right API signature accepts an int n, so handle it as best we can. + for i := n - 1; i > 0; i-- { + j := int(r.uint64n(uint64(i + 1))) + swap(i, j) + } +} + +/* + * Top-level convenience functions + */ + +// globalRand is the source of random numbers for the top-level +// convenience functions. +var globalRand = &Rand{src: &PCG{}} + +// Int64 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. +func Int64() int64 { return globalRand.Int64() } + +// Uint32 returns a pseudo-random 32-bit value as a uint32 +// from the default Source. +func Uint32() uint32 { return globalRand.Uint32() } + +// Uint64N returns, as a uint64, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Uint64N(n uint64) uint64 { return globalRand.Uint64N(n) } + +// Uint32N returns, as a uint32, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Uint32N(n uint32) uint32 { return globalRand.Uint32N(n) } + +// Uint64 returns a pseudo-random 64-bit value as a uint64 +// from the default Source. +func Uint64() uint64 { return globalRand.Uint64() } + +// Int32 returns a non-negative pseudo-random 31-bit integer as an int32 +// from the default Source. +func Int32() int32 { return globalRand.Int32() } + +// Int returns a non-negative pseudo-random int from the default Source. +func Int() int { return globalRand.Int() } + +// Int64N returns, as an int64, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Int64N(n int64) int64 { return globalRand.Int64N(n) } + +// Int32N returns, as an int32, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Int32N(n int32) int32 { return globalRand.Int32N(n) } + +// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func IntN(n int) int { return globalRand.IntN(n) } + +// UintN returns, as a uint, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func UintN(n uint) uint { return globalRand.UintN(n) } + +// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0) +// from the default Source. +func Float64() float64 { return globalRand.Float64() } + +// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0) +// from the default Source. +func Float32() float32 { return globalRand.Float32() } + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers +// in the half-open interval [0,n) from the default Source. +func Perm(n int) []int { return globalRand.Perm(n) } + +// Shuffle pseudo-randomizes the order of elements using the default Source. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func Shuffle(n int, swap func(i, j int)) { globalRand.Shuffle(n, swap) } + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1) +// from the default Source. +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func NormFloat64() float64 { return globalRand.NormFloat64() } + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func ExpFloat64() float64 { return globalRand.ExpFloat64() } diff --git a/gnovm/stdlibs/math/rand/rand_test.gno b/gnovm/stdlibs/math/rand/rand_test.gno new file mode 100644 index 00000000000..6e2e9cd4079 --- /dev/null +++ b/gnovm/stdlibs/math/rand/rand_test.gno @@ -0,0 +1,711 @@ +// Copyright 2009 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 + +import ( + "errors" + "fmt" + "math" + "os" + "testing" +) + +const ( + numTestSamples = 10000 +) + +type statsResults struct { + mean float64 + stddev float64 + closeEnough float64 + maxError float64 +} + +func max(a, b float64) float64 { + if a > b { + return a + } + return b +} + +func nearEqual(a, b, closeEnough, maxError float64) bool { + absDiff := math.Abs(a - b) + if absDiff < closeEnough { // Necessary when one value is zero and one value is close to zero. + return true + } + return absDiff/max(math.Abs(a), math.Abs(b)) < maxError +} + +var testSeeds = []uint64{1, 1754801282, 1698661970, 1550503961} + +// checkSimilarDistribution returns success if the mean and stddev of the +// two statsResults are similar. +func (this *statsResults) checkSimilarDistribution(expected *statsResults) error { + if !nearEqual(this.mean, expected.mean, expected.closeEnough, expected.maxError) { + s := fmt.Sprintf("mean %v != %v (allowed error %v, %v)", this.mean, expected.mean, expected.closeEnough, expected.maxError) + fmt.Println(s) + return errors.New(s) + } + if !nearEqual(this.stddev, expected.stddev, expected.closeEnough, expected.maxError) { + s := fmt.Sprintf("stddev %v != %v (allowed error %v, %v)", this.stddev, expected.stddev, expected.closeEnough, expected.maxError) + fmt.Println(s) + return errors.New(s) + } + return nil +} + +func getStatsResults(samples []float64) *statsResults { + res := new(statsResults) + var sum, squaresum float64 + for _, s := range samples { + sum += s + squaresum += s * s + } + res.mean = sum / float64(len(samples)) + res.stddev = math.Sqrt(squaresum/float64(len(samples)) - res.mean*res.mean) + return res +} + +func checkSampleDistribution(t *testing.T, samples []float64, expected *statsResults) { + t.Helper() + actual := getStatsResults(samples) + err := actual.checkSimilarDistribution(expected) + if err != nil { + t.Errorf(err.Error()) + } +} + +func checkSampleSliceDistributions(t *testing.T, samples []float64, nslices int, expected *statsResults) { + t.Helper() + chunk := len(samples) / nslices + for i := 0; i < nslices; i++ { + low := i * chunk + var high int + if i == nslices-1 { + high = len(samples) - 1 + } else { + high = (i + 1) * chunk + } + checkSampleDistribution(t, samples[low:high], expected) + } +} + +// +// Normal distribution tests +// + +func generateNormalSamples(nsamples int, mean, stddev float64, seed uint64) []float64 { + r := New(NewPCG(seed, seed)) + samples := make([]float64, nsamples) + for i := range samples { + samples[i] = r.NormFloat64()*stddev + mean + } + return samples +} + +func testNormalDistribution(t *testing.T, nsamples int, mean, stddev float64, seed uint64) { + // fmt.Printf("testing nsamples=%v mean=%v stddev=%v seed=%v\n", nsamples, mean, stddev, seed); + + samples := generateNormalSamples(nsamples, mean, stddev, seed) + errorScale := max(1.0, stddev) // Error scales with stddev + expected := &statsResults{mean, stddev, 0.10 * errorScale, 0.08 * errorScale} + + // Make sure that the entire set matches the expected distribution. + checkSampleDistribution(t, samples, expected) + + // Make sure that each half of the set matches the expected distribution. + checkSampleSliceDistributions(t, samples, 2, expected) + + // Make sure that each 7th of the set matches the expected distribution. + checkSampleSliceDistributions(t, samples, 7, expected) +} + +// Actual tests + +func TestStandardNormalValues(t *testing.T) { + for _, seed := range testSeeds { + testNormalDistribution(t, numTestSamples, 0, 1, seed) + } +} + +func TestNonStandardNormalValues(t *testing.T) { + sdmax := 1000.0 + mmax := 1000.0 + if testing.Short() { + sdmax = 5 + mmax = 5 + } + for sd := 0.5; sd < sdmax; sd *= 2 { + for m := 0.5; m < mmax; m *= 2 { + for _, seed := range testSeeds { + testNormalDistribution(t, numTestSamples, m, sd, seed) + if testing.Short() { + break + } + } + } + } +} + +// +// Exponential distribution tests +// + +func generateExponentialSamples(nsamples int, rate float64, seed uint64) []float64 { + r := New(NewPCG(seed, seed)) + samples := make([]float64, nsamples) + for i := range samples { + samples[i] = r.ExpFloat64() / rate + } + return samples +} + +func testExponentialDistribution(t *testing.T, nsamples int, rate float64, seed uint64) { + // fmt.Printf("testing nsamples=%v rate=%v seed=%v\n", nsamples, rate, seed); + + mean := 1 / rate + stddev := mean + + samples := generateExponentialSamples(nsamples, rate, seed) + errorScale := max(1.0, 1/rate) // Error scales with the inverse of the rate + expected := &statsResults{mean, stddev, 0.10 * errorScale, 0.20 * errorScale} + + // Make sure that the entire set matches the expected distribution. + checkSampleDistribution(t, samples, expected) + + // Make sure that each half of the set matches the expected distribution. + checkSampleSliceDistributions(t, samples, 2, expected) + + // Make sure that each 7th of the set matches the expected distribution. + checkSampleSliceDistributions(t, samples, 7, expected) +} + +// Actual tests + +func TestStandardExponentialValues(t *testing.T) { + for _, seed := range testSeeds { + testExponentialDistribution(t, numTestSamples, 1, seed) + } +} + +func TestNonStandardExponentialValues(t *testing.T) { + for rate := 0.05; rate < 10; rate *= 2 { + for _, seed := range testSeeds { + testExponentialDistribution(t, numTestSamples, rate, seed) + if testing.Short() { + break + } + } + } +} + +// +// Table generation tests +// + +func initNorm() (testKn []uint32, testWn, testFn []float32) { + const m1 = 1 << 31 + var ( + dn float64 = rn + tn = dn + vn float64 = 9.91256303526217e-3 + ) + + testKn = make([]uint32, 128) + testWn = make([]float32, 128) + testFn = make([]float32, 128) + + q := vn / math.Exp(-0.5*dn*dn) + testKn[0] = uint32((dn / q) * m1) + testKn[1] = 0 + testWn[0] = float32(q / m1) + testWn[127] = float32(dn / m1) + testFn[0] = 1.0 + testFn[127] = float32(math.Exp(-0.5 * dn * dn)) + for i := 126; i >= 1; i-- { + dn = math.Sqrt(-2.0 * math.Log(vn/dn+math.Exp(-0.5*dn*dn))) + testKn[i+1] = uint32((dn / tn) * m1) + tn = dn + testFn[i] = float32(math.Exp(-0.5 * dn * dn)) + testWn[i] = float32(dn / m1) + } + return +} + +func initExp() (testKe []uint32, testWe, testFe []float32) { + const m2 = 1 << 32 + var ( + de float64 = re + te = de + ve float64 = 3.9496598225815571993e-3 + ) + + testKe = make([]uint32, 256) + testWe = make([]float32, 256) + testFe = make([]float32, 256) + + q := ve / math.Exp(-de) + testKe[0] = uint32((de / q) * m2) + testKe[1] = 0 + testWe[0] = float32(q / m2) + testWe[255] = float32(de / m2) + testFe[0] = 1.0 + testFe[255] = float32(math.Exp(-de)) + for i := 254; i >= 1; i-- { + de = -math.Log(ve/de + math.Exp(-de)) + testKe[i+1] = uint32((de / te) * m2) + te = de + testFe[i] = float32(math.Exp(-de)) + testWe[i] = float32(de / m2) + } + return +} + +// compareUint32Slices returns the first index where the two slices +// disagree, or <0 if the lengths are the same and all elements +// are identical. +func compareUint32Slices(s1, s2 []uint32) int { + if len(s1) != len(s2) { + if len(s1) > len(s2) { + return len(s2) + 1 + } + return len(s1) + 1 + } + for i := range s1 { + if s1[i] != s2[i] { + return i + } + } + return -1 +} + +// compareFloat32Slices returns the first index where the two slices +// disagree, or <0 if the lengths are the same and all elements +// are identical. +func compareFloat32Slices(s1, s2 []float32) int { + if len(s1) != len(s2) { + if len(s1) > len(s2) { + return len(s2) + 1 + } + return len(s1) + 1 + } + for i := range s1 { + if !nearEqual(float64(s1[i]), float64(s2[i]), 0, 1e-7) { + return i + } + } + return -1 +} + +func TestNormTables(t *testing.T) { + testKn, testWn, testFn := initNorm() + if i := compareUint32Slices(kn[0:], testKn); i >= 0 { + t.Errorf("kn disagrees at index %v; %v != %v", i, kn[i], testKn[i]) + } + if i := compareFloat32Slices(wn[0:], testWn); i >= 0 { + t.Errorf("wn disagrees at index %v; %v != %v", i, wn[i], testWn[i]) + } + if i := compareFloat32Slices(fn[0:], testFn); i >= 0 { + t.Errorf("fn disagrees at index %v; %v != %v", i, fn[i], testFn[i]) + } +} + +func TestExpTables(t *testing.T) { + testKe, testWe, testFe := initExp() + if i := compareUint32Slices(ke[0:], testKe); i >= 0 { + t.Errorf("ke disagrees at index %v; %v != %v", i, ke[i], testKe[i]) + } + if i := compareFloat32Slices(we[0:], testWe); i >= 0 { + t.Errorf("we disagrees at index %v; %v != %v", i, we[i], testWe[i]) + } + if i := compareFloat32Slices(fe[0:], testFe); i >= 0 { + t.Errorf("fe disagrees at index %v; %v != %v", i, fe[i], testFe[i]) + } +} + +func TestFloat32(t *testing.T) { + // For issue 6721, the problem came after 7533753 calls, so check 10e6. + num := int(10e6) + + // XXX: This will take a lot of time on Gno, so reduce by 100 from original. + num /= 100 // 1.72 seconds instead of 172 seconds + + r := testRand() + for ct := 0; ct < num; ct++ { + f := r.Float32() + if f >= 1 { + t.Fatal("Float32() should be in range [0,1). ct:", ct, "f:", f) + } + } +} + +func TestShuffleSmall(t *testing.T) { + // Check that Shuffle allows n=0 and n=1, but that swap is never called for them. + r := testRand() + for n := 0; n <= 1; n++ { + r.Shuffle(n, func(i, j int) { t.Fatalf("swap called, n=%d i=%d j=%d", n, i, j) }) + } +} + +// encodePerm converts from a permuted slice of length n, such as Perm generates, to an int in [0, n!). +// See https://en.wikipedia.org/wiki/Lehmer_code. +// encodePerm modifies the input slice. +func encodePerm(s []int) int { + // Convert to Lehmer code. + for i, x := range s { + r := s[i+1:] + for j, y := range r { + if y > x { + r[j]-- + } + } + } + // Convert to int in [0, n!). + m := 0 + fact := 1 + for i := len(s) - 1; i >= 0; i-- { + m += s[i] * fact + fact *= len(s) - i + } + return m +} + +// TestUniformFactorial tests several ways of generating a uniform value in [0, n!). +func TestUniformFactorial(t *testing.T) { + t.Skip("This test takes ~3 mins to run on Gno at time of writing, even in its short form") + + r := New(NewPCG(1, 2)) + top := 6 + if testing.Short() { + top = 3 + } + for n := 3; n <= top; n++ { + t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) { + // Calculate n!. + nfact := 1 + for i := 2; i <= n; i++ { + nfact *= i + } + + // Test a few different ways to generate a uniform distribution. + p := make([]int, n) // re-usable slice for Shuffle generator + tests := [...]struct { + name string + fn func() int + }{ + {name: "Int32N", fn: func() int { return int(r.Int32N(int32(nfact))) }}, + {name: "Perm", fn: func() int { return encodePerm(r.Perm(n)) }}, + {name: "Shuffle", fn: func() int { + // Generate permutation using Shuffle. + for i := range p { + p[i] = i + } + r.Shuffle(n, func(i, j int) { p[i], p[j] = p[j], p[i] }) + return encodePerm(p) + }}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Gather chi-squared values and check that they follow + // the expected normal distribution given n!-1 degrees of freedom. + // See https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test and + // https://www.johndcook.com/Beautiful_Testing_ch10.pdf. + nsamples := 10 * nfact + if nsamples < 1000 { + nsamples = 1000 + } + samples := make([]float64, nsamples) + for i := range samples { + // Generate some uniformly distributed values and count their occurrences. + const iters = 1000 + counts := make([]int, nfact) + for i := 0; i < iters; i++ { + counts[test.fn()]++ + } + // Calculate chi-squared and add to samples. + want := iters / float64(nfact) + var χ2 float64 + for _, have := range counts { + err := float64(have) - want + χ2 += err * err + } + χ2 /= want + samples[i] = χ2 + } + + // Check that our samples approximate the appropriate normal distribution. + dof := float64(nfact - 1) + expected := &statsResults{mean: dof, stddev: math.Sqrt(2 * dof)} + errorScale := max(1.0, expected.stddev) + expected.closeEnough = 0.10 * errorScale + expected.maxError = 0.08 // TODO: What is the right value here? See issue 21211. + checkSampleDistribution(t, samples, expected) + }) + } + }) + } +} + +// Benchmarks + +var Sink uint64 + +func testRand() *Rand { + return New(NewPCG(1, 2)) +} + +func BenchmarkSourceUint64(b *testing.B) { + s := NewPCG(1, 2) + var t uint64 + for n := b.N; n > 0; n-- { + t += s.Uint64() + } + Sink = uint64(t) +} + +func BenchmarkGlobalInt64(b *testing.B) { + var t int64 + for n := b.N; n > 0; n-- { + t += Int64() + } + Sink = uint64(t) +} + +func BenchmarkGlobalUint64(b *testing.B) { + var t uint64 + for n := b.N; n > 0; n-- { + t += Uint64() + } + Sink = t +} + +func BenchmarkInt64(b *testing.B) { + r := testRand() + var t int64 + for n := b.N; n > 0; n-- { + t += r.Int64() + } + Sink = uint64(t) +} + +func BenchmarkUint64(b *testing.B) { + r := testRand() + var t uint64 + for n := b.N; n > 0; n-- { + t += r.Uint64() + } + Sink = t +} + +func BenchmarkGlobalIntN1000(b *testing.B) { + var t int + arg := 1000 + for n := b.N; n > 0; n-- { + t += IntN(arg) + } + Sink = uint64(t) +} + +func BenchmarkIntN1000(b *testing.B) { + r := testRand() + var t int + arg := 1000 + for n := b.N; n > 0; n-- { + t += r.IntN(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N1000(b *testing.B) { + r := testRand() + var t int64 + arg := int64(1000) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N1e8(b *testing.B) { + r := testRand() + var t int64 + arg := int64(1e8) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N1e9(b *testing.B) { + r := testRand() + var t int64 + arg := int64(1e9) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N2e9(b *testing.B) { + r := testRand() + var t int64 + arg := int64(2e9) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N1e18(b *testing.B) { + r := testRand() + var t int64 + arg := int64(1e18) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N2e18(b *testing.B) { + r := testRand() + var t int64 + arg := int64(2e18) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt64N4e18(b *testing.B) { + r := testRand() + var t int64 + arg := int64(4e18) + for n := b.N; n > 0; n-- { + t += r.Int64N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt32N1000(b *testing.B) { + r := testRand() + var t int32 + arg := int32(1000) + for n := b.N; n > 0; n-- { + t += r.Int32N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt32N1e8(b *testing.B) { + r := testRand() + var t int32 + arg := int32(1e8) + for n := b.N; n > 0; n-- { + t += r.Int32N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt32N1e9(b *testing.B) { + r := testRand() + var t int32 + arg := int32(1e9) + for n := b.N; n > 0; n-- { + t += r.Int32N(arg) + } + Sink = uint64(t) +} + +func BenchmarkInt32N2e9(b *testing.B) { + r := testRand() + var t int32 + arg := int32(2e9) + for n := b.N; n > 0; n-- { + t += r.Int32N(arg) + } + Sink = uint64(t) +} + +func BenchmarkFloat32(b *testing.B) { + r := testRand() + var t float32 + for n := b.N; n > 0; n-- { + t += r.Float32() + } + Sink = uint64(t) +} + +func BenchmarkFloat64(b *testing.B) { + r := testRand() + var t float64 + for n := b.N; n > 0; n-- { + t += r.Float64() + } + Sink = uint64(t) +} + +func BenchmarkExpFloat64(b *testing.B) { + r := testRand() + var t float64 + for n := b.N; n > 0; n-- { + t += r.ExpFloat64() + } + Sink = uint64(t) +} + +func BenchmarkNormFloat64(b *testing.B) { + r := testRand() + var t float64 + for n := b.N; n > 0; n-- { + t += r.NormFloat64() + } + Sink = uint64(t) +} + +func BenchmarkPerm3(b *testing.B) { + r := testRand() + var t int + for n := b.N; n > 0; n-- { + t += r.Perm(3)[0] + } + Sink = uint64(t) +} + +func BenchmarkPerm30(b *testing.B) { + r := testRand() + var t int + for n := b.N; n > 0; n-- { + t += r.Perm(30)[0] + } + Sink = uint64(t) +} + +func BenchmarkPerm30ViaShuffle(b *testing.B) { + r := testRand() + var t int + for n := b.N; n > 0; n-- { + p := make([]int, 30) + for i := range p { + p[i] = i + } + r.Shuffle(30, func(i, j int) { p[i], p[j] = p[j], p[i] }) + t += p[0] + } + Sink = uint64(t) +} + +// BenchmarkShuffleOverhead uses a minimal swap function +// to measure just the shuffling overhead. +func BenchmarkShuffleOverhead(b *testing.B) { + r := testRand() + for n := b.N; n > 0; n-- { + r.Shuffle(30, func(i, j int) { + if i < 0 || i >= 30 || j < 0 || j >= 30 { + b.Fatalf("bad swap(%d, %d)", i, j) + } + }) + } +} diff --git a/gnovm/stdlibs/math/rand/regress_test.gno b/gnovm/stdlibs/math/rand/regress_test.gno new file mode 100644 index 00000000000..256500de66b --- /dev/null +++ b/gnovm/stdlibs/math/rand/regress_test.gno @@ -0,0 +1,478 @@ +// Copyright 2014 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. + +// Test that random number sequences generated by a specific seed +// do not change from version to version. +// +// Do NOT make changes to the golden outputs. If bugs need to be fixed +// in the underlying code, find ways to fix them that do not affect the +// outputs. + +package rand + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "testing" +) + +/* +XXX: disabled because reflect, but would be good to enable it +switching reflect to simple fn calls. + +func TestRegress(t *testing.T) { + int32s := []int32{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1} + uint32s := []uint32{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1<<32 - 2, 1<<32 - 1} + int64s := []int64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<63 - 2, 1<<63 - 1} + uint64s := []uint64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<63 - 2, 1<<63 - 1, 1<<64 - 2, 1<<64 - 1} + permSizes := []int{0, 1, 5, 8, 9, 10, 16} + + n := reflect.TypeOf(New(NewPCG(1, 2))).NumMethod() + p := 0 + var buf bytes.Buffer + for i := 0; i < n; i++ { + if *update && i > 0 { + fmt.Fprintf(&buf, "\n") + } + r := New(NewPCG(1, 2)) + rv := reflect.ValueOf(r) + m := rv.Type().Method(i) + mv := rv.Method(i) + mt := mv.Type() + if mt.NumOut() == 0 { + continue + } + for repeat := 0; repeat < 20; repeat++ { + var args []reflect.Value + var argstr string + if mt.NumIn() == 1 { + var x any + switch mt.In(0).Kind() { + default: + t.Fatalf("unexpected argument type for r.%s", m.Name) + + case reflect.Int: + if m.Name == "Perm" { + x = permSizes[repeat%len(permSizes)] + break + } + big := int64s[repeat%len(int64s)] + if int64(int(big)) != big { + // On 32-bit machine. + // Consume an Int64 like on a 64-bit machine, + // to keep the golden data the same on different architectures. + r.Int64N(big) + if *update { + t.Fatalf("must run -update on 64-bit machine") + } + p++ + continue + } + x = int(big) + + case reflect.Uint: + big := uint64s[repeat%len(uint64s)] + if uint64(uint(big)) != big { + r.Uint64N(big) // what would happen on 64-bit machine, to keep stream in sync + if *update { + t.Fatalf("must run -update on 64-bit machine") + } + p++ + continue + } + x = uint(big) + + case reflect.Int32: + x = int32s[repeat%len(int32s)] + + case reflect.Int64: + x = int64s[repeat%len(int64s)] + + case reflect.Uint32: + x = uint32s[repeat%len(uint32s)] + + case reflect.Uint64: + x = uint64s[repeat%len(uint64s)] + } + argstr = fmt.Sprint(x) + args = append(args, reflect.ValueOf(x)) + } + + var out any + out = mv.Call(args)[0].Interface() + if m.Name == "Int" || m.Name == "IntN" { + out = int64(out.(int)) + } + if m.Name == "Uint" || m.Name == "UintN" { + out = uint64(out.(uint)) + } + if *update { + var val string + big := int64(1 << 60) + if int64(int(big)) != big && (m.Name == "Int" || m.Name == "IntN") { + // 32-bit machine cannot print 64-bit results + val = "truncated" + } else if reflect.TypeOf(out).Kind() == reflect.Slice { + val = fmt.Sprintf("%#v", out) + } else { + val = fmt.Sprintf("%T(%v)", out, out) + } + fmt.Fprintf(&buf, "\t%s, // %s(%s)\n", val, m.Name, argstr) + } else if p >= len(regressGolden) { + t.Errorf("r.%s(%s) = %v, missing golden value", m.Name, argstr, out) + } else { + want := regressGolden[p] + if m.Name == "Int" { + want = int64(int(uint(want.(int64)) << 1 >> 1)) + } + if !reflect.DeepEqual(out, want) { + t.Errorf("r.%s(%s) = %v, want %v", m.Name, argstr, out, want) + } + } + p++ + } + } +} +*/ + +var regressGolden = []interface{}{ + float64(0.5931317151369719), // ExpFloat64() + float64(0.0680034588807843), // ExpFloat64() + float64(0.036496967459790364), // ExpFloat64() + float64(2.460335459645379), // ExpFloat64() + float64(1.5792300208419903), // ExpFloat64() + float64(0.9149501499404387), // ExpFloat64() + float64(0.43463410545541104), // ExpFloat64() + float64(0.5513632046504593), // ExpFloat64() + float64(0.7426404617374481), // ExpFloat64() + float64(1.2334925132631804), // ExpFloat64() + float64(0.892529142200442), // ExpFloat64() + float64(0.21508763681487764), // ExpFloat64() + float64(1.0208588200798545), // ExpFloat64() + float64(0.7650739736831382), // ExpFloat64() + float64(0.7772788529257701), // ExpFloat64() + float64(1.102732861281323), // ExpFloat64() + float64(0.6982243043885805), // ExpFloat64() + float64(0.4981788638202421), // ExpFloat64() + float64(0.15806532306947937), // ExpFloat64() + float64(0.9419163802459202), // ExpFloat64() + + float32(0.95955694), // Float32() + float32(0.8076733), // Float32() + float32(0.8135684), // Float32() + float32(0.92872405), // Float32() + float32(0.97472525), // Float32() + float32(0.5485458), // Float32() + float32(0.97740936), // Float32() + float32(0.042272687), // Float32() + float32(0.99663067), // Float32() + float32(0.035181105), // Float32() + float32(0.45059562), // Float32() + float32(0.86597633), // Float32() + float32(0.8954844), // Float32() + float32(0.090798736), // Float32() + float32(0.46218646), // Float32() + float32(0.5955118), // Float32() + float32(0.08985227), // Float32() + float32(0.19820237), // Float32() + float32(0.7443699), // Float32() + float32(0.56461), // Float32() + + float64(0.6764556596678251), // Float64() + float64(0.4613862177205994), // Float64() + float64(0.5085473976760264), // Float64() + float64(0.4297927436037299), // Float64() + float64(0.797802349388613), // Float64() + float64(0.3883664855410056), // Float64() + float64(0.8192750264193612), // Float64() + float64(0.3381816951746133), // Float64() + float64(0.9730458047755973), // Float64() + float64(0.281449117585586), // Float64() + float64(0.6047654075331631), // Float64() + float64(0.9278107175107462), // Float64() + float64(0.16387541502137226), // Float64() + float64(0.7263900707339023), // Float64() + float64(0.6974917552729882), // Float64() + float64(0.7640946923790318), // Float64() + float64(0.7188183661358182), // Float64() + float64(0.5856191500346635), // Float64() + float64(0.9549597149363428), // Float64() + float64(0.5168804691962643), // Float64() + + int64(4969059760275911952), // Int() + int64(2147869220224756844), // Int() + int64(5246770554000605320), // Int() + int64(5471241176507662746), // Int() + int64(4321634407747778896), // Int() + int64(760102831717374652), // Int() + int64(9221744211007427193), // Int() + int64(8289669384274456462), // Int() + int64(2449715415482412441), // Int() + int64(3389241988064777392), // Int() + int64(2986830195847294191), // Int() + int64(8204908297817606218), // Int() + int64(8134976985547166651), // Int() + int64(2240328155279531677), // Int() + int64(7311121042813227358), // Int() + int64(5231057920893523323), // Int() + int64(4257872588489500903), // Int() + int64(158397175702351138), // Int() + int64(1350674201389090105), // Int() + int64(6093522341581845358), // Int() + + int32(1652216515), // Int32() + int32(1323786710), // Int32() + int32(1684546306), // Int32() + int32(1710678126), // Int32() + int32(503104460), // Int32() + int32(88487615), // Int32() + int32(1073552320), // Int32() + int32(965044529), // Int32() + int32(285184408), // Int32() + int32(394559696), // Int32() + int32(1421454622), // Int32() + int32(955177040), // Int32() + int32(2020777787), // Int32() + int32(260808523), // Int32() + int32(851126509), // Int32() + int32(1682717115), // Int32() + int32(1569423431), // Int32() + int32(1092181682), // Int32() + int32(157239171), // Int32() + int32(709379364), // Int32() + + int32(0), // Int32N(1) + int32(6), // Int32N(10) + int32(8), // Int32N(32) + int32(704922), // Int32N(1048576) + int32(245656), // Int32N(1048577) + int32(41205257), // Int32N(1000000000) + int32(43831929), // Int32N(1073741824) + int32(965044528), // Int32N(2147483646) + int32(285184408), // Int32N(2147483647) + int32(0), // Int32N(1) + int32(6), // Int32N(10) + int32(10), // Int32N(32) + int32(283579), // Int32N(1048576) + int32(127348), // Int32N(1048577) + int32(396336665), // Int32N(1000000000) + int32(911873403), // Int32N(1073741824) + int32(1569423430), // Int32N(2147483646) + int32(1092181681), // Int32N(2147483647) + int32(0), // Int32N(1) + int32(3), // Int32N(10) + + int64(4969059760275911952), // Int64() + int64(2147869220224756844), // Int64() + int64(5246770554000605320), // Int64() + int64(5471241176507662746), // Int64() + int64(4321634407747778896), // Int64() + int64(760102831717374652), // Int64() + int64(9221744211007427193), // Int64() + int64(8289669384274456462), // Int64() + int64(2449715415482412441), // Int64() + int64(3389241988064777392), // Int64() + int64(2986830195847294191), // Int64() + int64(8204908297817606218), // Int64() + int64(8134976985547166651), // Int64() + int64(2240328155279531677), // Int64() + int64(7311121042813227358), // Int64() + int64(5231057920893523323), // Int64() + int64(4257872588489500903), // Int64() + int64(158397175702351138), // Int64() + int64(1350674201389090105), // Int64() + int64(6093522341581845358), // Int64() + + int64(0), // Int64N(1) + int64(6), // Int64N(10) + int64(8), // Int64N(32) + int64(704922), // Int64N(1048576) + int64(245656), // Int64N(1048577) + int64(41205257), // Int64N(1000000000) + int64(43831929), // Int64N(1073741824) + int64(965044528), // Int64N(2147483646) + int64(285184408), // Int64N(2147483647) + int64(183731176326946086), // Int64N(1000000000000000000) + int64(680987186633600239), // Int64N(1152921504606846976) + int64(4102454148908803108), // Int64N(9223372036854775806) + int64(8679174511200971228), // Int64N(9223372036854775807) + int64(0), // Int64N(1) + int64(3), // Int64N(10) + int64(27), // Int64N(32) + int64(665831), // Int64N(1048576) + int64(533292), // Int64N(1048577) + int64(73220195), // Int64N(1000000000) + int64(686060398), // Int64N(1073741824) + + int64(0), // IntN(1) + int64(6), // IntN(10) + int64(8), // IntN(32) + int64(704922), // IntN(1048576) + int64(245656), // IntN(1048577) + int64(41205257), // IntN(1000000000) + int64(43831929), // IntN(1073741824) + int64(965044528), // IntN(2147483646) + int64(285184408), // IntN(2147483647) + int64(183731176326946086), // IntN(1000000000000000000) + int64(680987186633600239), // IntN(1152921504606846976) + int64(4102454148908803108), // IntN(9223372036854775806) + int64(8679174511200971228), // IntN(9223372036854775807) + int64(0), // IntN(1) + int64(3), // IntN(10) + int64(27), // IntN(32) + int64(665831), // IntN(1048576) + int64(533292), // IntN(1048577) + int64(73220195), // IntN(1000000000) + int64(686060398), // IntN(1073741824) + + float64(0.37944549835531083), // NormFloat64() + float64(0.07473804659119399), // NormFloat64() + float64(0.20006841200604142), // NormFloat64() + float64(-1.1253144115495104), // NormFloat64() + float64(-0.4005883316435388), // NormFloat64() + float64(-3.0853771402394736), // NormFloat64() + float64(1.932330243076978), // NormFloat64() + float64(1.726131393719264), // NormFloat64() + float64(-0.11707238034168332), // NormFloat64() + float64(-0.9303318111676635), // NormFloat64() + float64(-0.04750789419852852), // NormFloat64() + float64(0.22248301107582735), // NormFloat64() + float64(-1.83630520614272), // NormFloat64() + float64(0.7259521217919809), // NormFloat64() + float64(0.8806882871913041), // NormFloat64() + float64(-1.5022903484270484), // NormFloat64() + float64(0.5972577266810571), // NormFloat64() + float64(1.5631937339973658), // NormFloat64() + float64(-0.3841235370075905), // NormFloat64() + float64(-0.2967295854430667), // NormFloat64() + + []int{}, // Perm(0) + []int{0}, // Perm(1) + []int{1, 4, 2, 0, 3}, // Perm(5) + []int{4, 3, 6, 1, 5, 2, 7, 0}, // Perm(8) + []int{6, 5, 1, 8, 7, 2, 0, 3, 4}, // Perm(9) + []int{9, 4, 2, 5, 6, 8, 1, 7, 0, 3}, // Perm(10) + []int{5, 9, 3, 1, 4, 2, 10, 7, 15, 11, 0, 14, 13, 8, 6, 12}, // Perm(16) + []int{}, // Perm(0) + []int{0}, // Perm(1) + []int{4, 2, 1, 3, 0}, // Perm(5) + []int{0, 2, 3, 1, 5, 4, 6, 7}, // Perm(8) + []int{2, 0, 8, 3, 4, 7, 6, 5, 1}, // Perm(9) + []int{0, 6, 5, 3, 8, 4, 1, 2, 9, 7}, // Perm(10) + []int{9, 14, 4, 11, 13, 8, 0, 6, 2, 12, 3, 7, 1, 10, 5, 15}, // Perm(16) + []int{}, // Perm(0) + []int{0}, // Perm(1) + []int{2, 4, 0, 3, 1}, // Perm(5) + []int{3, 2, 1, 0, 7, 5, 4, 6}, // Perm(8) + []int{1, 3, 4, 5, 0, 2, 7, 8, 6}, // Perm(9) + []int{1, 8, 4, 7, 2, 6, 5, 9, 0, 3}, // Perm(10) + + uint32(3304433030), // Uint32() + uint32(2647573421), // Uint32() + uint32(3369092613), // Uint32() + uint32(3421356252), // Uint32() + uint32(1006208920), // Uint32() + uint32(176975231), // Uint32() + uint32(2147104640), // Uint32() + uint32(1930089058), // Uint32() + uint32(570368816), // Uint32() + uint32(789119393), // Uint32() + uint32(2842909244), // Uint32() + uint32(1910354080), // Uint32() + uint32(4041555575), // Uint32() + uint32(521617046), // Uint32() + uint32(1702253018), // Uint32() + uint32(3365434230), // Uint32() + uint32(3138846863), // Uint32() + uint32(2184363364), // Uint32() + uint32(314478343), // Uint32() + uint32(1418758728), // Uint32() + + uint32(0), // Uint32N(1) + uint32(6), // Uint32N(10) + uint32(8), // Uint32N(32) + uint32(704922), // Uint32N(1048576) + uint32(245656), // Uint32N(1048577) + uint32(41205257), // Uint32N(1000000000) + uint32(43831929), // Uint32N(1073741824) + uint32(965044528), // Uint32N(2147483646) + uint32(285184408), // Uint32N(2147483647) + uint32(789119393), // Uint32N(4294967294) + uint32(2842909244), // Uint32N(4294967295) + uint32(0), // Uint32N(1) + uint32(9), // Uint32N(10) + uint32(29), // Uint32N(32) + uint32(266590), // Uint32N(1048576) + uint32(821640), // Uint32N(1048577) + uint32(730819735), // Uint32N(1000000000) + uint32(522841378), // Uint32N(1073741824) + uint32(157239171), // Uint32N(2147483646) + uint32(709379364), // Uint32N(2147483647) + + uint64(14192431797130687760), // Uint64() + uint64(11371241257079532652), // Uint64() + uint64(14470142590855381128), // Uint64() + uint64(14694613213362438554), // Uint64() + uint64(4321634407747778896), // Uint64() + uint64(760102831717374652), // Uint64() + uint64(9221744211007427193), // Uint64() + uint64(8289669384274456462), // Uint64() + uint64(2449715415482412441), // Uint64() + uint64(3389241988064777392), // Uint64() + uint64(12210202232702069999), // Uint64() + uint64(8204908297817606218), // Uint64() + uint64(17358349022401942459), // Uint64() + uint64(2240328155279531677), // Uint64() + uint64(7311121042813227358), // Uint64() + uint64(14454429957748299131), // Uint64() + uint64(13481244625344276711), // Uint64() + uint64(9381769212557126946), // Uint64() + uint64(1350674201389090105), // Uint64() + uint64(6093522341581845358), // Uint64() + + uint64(0), // Uint64N(1) + uint64(6), // Uint64N(10) + uint64(8), // Uint64N(32) + uint64(704922), // Uint64N(1048576) + uint64(245656), // Uint64N(1048577) + uint64(41205257), // Uint64N(1000000000) + uint64(43831929), // Uint64N(1073741824) + uint64(965044528), // Uint64N(2147483646) + uint64(285184408), // Uint64N(2147483647) + uint64(183731176326946086), // Uint64N(1000000000000000000) + uint64(680987186633600239), // Uint64N(1152921504606846976) + uint64(4102454148908803108), // Uint64N(9223372036854775806) + uint64(8679174511200971228), // Uint64N(9223372036854775807) + uint64(2240328155279531676), // Uint64N(18446744073709551614) + uint64(7311121042813227357), // Uint64N(18446744073709551615) + uint64(0), // Uint64N(1) + uint64(7), // Uint64N(10) + uint64(2), // Uint64N(32) + uint64(312633), // Uint64N(1048576) + uint64(346376), // Uint64N(1048577) + + uint64(0), // UintN(1) + uint64(6), // UintN(10) + uint64(8), // UintN(32) + uint64(704922), // UintN(1048576) + uint64(245656), // UintN(1048577) + uint64(41205257), // UintN(1000000000) + uint64(43831929), // UintN(1073741824) + uint64(965044528), // UintN(2147483646) + uint64(285184408), // UintN(2147483647) + uint64(183731176326946086), // UintN(1000000000000000000) + uint64(680987186633600239), // UintN(1152921504606846976) + uint64(4102454148908803108), // UintN(9223372036854775806) + uint64(8679174511200971228), // UintN(9223372036854775807) + uint64(2240328155279531676), // UintN(18446744073709551614) + uint64(7311121042813227357), // UintN(18446744073709551615) + uint64(0), // UintN(1) + uint64(7), // UintN(10) + uint64(2), // UintN(32) + uint64(312633), // UintN(1048576) + uint64(346376), // UintN(1048577) +} diff --git a/gnovm/stdlibs/math/rand/zipf.gno b/gnovm/stdlibs/math/rand/zipf.gno new file mode 100644 index 00000000000..f04c814eb75 --- /dev/null +++ b/gnovm/stdlibs/math/rand/zipf.gno @@ -0,0 +1,77 @@ +// Copyright 2009 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. + +// W.Hormann, G.Derflinger: +// "Rejection-Inversion to Generate Variates +// from Monotone Discrete Distributions" +// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz + +package rand + +import "math" + +// A Zipf generates Zipf distributed variates. +type Zipf struct { + r *Rand + imax float64 + v float64 + q float64 + s float64 + oneminusQ float64 + oneminusQinv float64 + hxm float64 + hx0minusHxm float64 +} + +func (z *Zipf) h(x float64) float64 { + return math.Exp(z.oneminusQ*math.Log(z.v+x)) * z.oneminusQinv +} + +func (z *Zipf) hinv(x float64) float64 { + return math.Exp(z.oneminusQinv*math.Log(z.oneminusQ*x)) - z.v +} + +// NewZipf returns a Zipf variate generator. +// The generator generates values k ∈ [0, imax] +// such that P(k) is proportional to (v + k) ** (-s). +// Requirements: s > 1 and v >= 1. +func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf { + z := new(Zipf) + if s <= 1.0 || v < 1 { + return nil + } + z.r = r + z.imax = float64(imax) + z.v = v + z.q = s + z.oneminusQ = 1.0 - z.q + z.oneminusQinv = 1.0 / z.oneminusQ + z.hxm = z.h(z.imax + 0.5) + z.hx0minusHxm = z.h(0.5) - math.Exp(math.Log(z.v)*(-z.q)) - z.hxm + z.s = 1 - z.hinv(z.h(1.5)-math.Exp(-z.q*math.Log(z.v+1.0))) + return z +} + +// Uint64 returns a value drawn from the Zipf distribution described +// by the Zipf object. +func (z *Zipf) Uint64() uint64 { + if z == nil { + panic("rand: nil Zipf") + } + k := 0.0 + + for { + r := z.r.Float64() // r on [0,1] + ur := z.hxm + r*z.hx0minusHxm + x := z.hinv(ur) + k = math.Floor(x + 0.5) + if k-x <= z.s { + break + } + if ur >= z.h(k+0.5)-math.Exp(-math.Log(k+z.v)*z.q) { + break + } + } + return uint64(k) +} diff --git a/gnovm/stdlibs/sort/sort_test.gno b/gnovm/stdlibs/sort/sort_test.gno index 8416f0b77e1..dac6ab11f67 100644 --- a/gnovm/stdlibs/sort/sort_test.gno +++ b/gnovm/stdlibs/sort/sort_test.gno @@ -98,7 +98,7 @@ func TestSortLarge_Random(t *testing.T) { } data := make([]int, n) for i := 0; i < len(data); i++ { - data[i] = rand.Intn(100) + data[i] = rand.IntN(100) } if sort.IntsAreSorted(data) { t.Fatalf("terrible rand.rand") @@ -160,7 +160,7 @@ func TestNonDeterministicComparison(t *testing.T) { }() td := &nonDeterministicTestingData{ - r: rand.New(rand.NewSource(0)), + r: rand.New(rand.NewPCG(0, 0)), } for i := 0; i < 10; i++ { @@ -377,13 +377,13 @@ func testBentleyMcIlroy(t *testing.T, sortFn func(sort.Interface), maxswap func( case _Sawtooth: data[i] = i % m case _Rand: - data[i] = rand.Intn(m) + data[i] = rand.IntN(m) case _Stagger: data[i] = (i*m + i) % n case _Plateau: data[i] = min(i, m) case _Shuffle: - if rand.Intn(m) != 0 { + if rand.IntN(m) != 0 { j += 2 data[i] = j } else { @@ -587,7 +587,7 @@ func TestStability(t *testing.T) { // random distribution for i := 0; i < len(data); i++ { - data[i].a = rand.Intn(m) + data[i].a = rand.IntN(m) } if sort.IsSorted(data) { t.Fatalf("terrible rand.rand") @@ -643,7 +643,7 @@ func countOps(t *testing.T, algo func(sort.Interface), name string) { maxswap: 1<<31 - 1, } for i := 0; i < n; i++ { - td.data[i] = rand.Intn(n / 5) + td.data[i] = rand.IntN(n / 5) } algo(&td) t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp) diff --git a/gnovm/tests/files/extern/p1/s2.gno b/gnovm/tests/files/extern/p1/s2.gno index cfed6498051..01aa3f47da1 100644 --- a/gnovm/tests/files/extern/p1/s2.gno +++ b/gnovm/tests/files/extern/p1/s2.gno @@ -3,5 +3,3 @@ package p1 import "math/rand" var Uint32 = rand.Uint32 - -func init() { rand.Seed(1) } diff --git a/gnovm/tests/files/import4.gno b/gnovm/tests/files/import4.gno index dc52adafc31..91364f45389 100644 --- a/gnovm/tests/files/import4.gno +++ b/gnovm/tests/files/import4.gno @@ -5,4 +5,4 @@ import "github.com/gnolang/gno/_test/p1" func main() { println("num:", p1.Uint32()) } // Output: -// num: 2596996162 +// num: 956301160 diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index 3dbd292ea68..86c81be9a18 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -23,7 +23,7 @@ import ( "log" "math" "math/big" - "math/rand" + "math/rand/v2" "net" "net/url" "os" @@ -114,7 +114,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkgPath == "encoding/xml" || pkgPath == "internal/os_test" || pkgPath == "math/big" || - pkgPath == "math/rand" || mode == ImportModeStdlibsPreferred || mode == ImportModeNativePreferred { switch pkgPath { @@ -271,12 +270,10 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri case "math/rand": // XXX only expose for tests. pkg := gno.NewPackageNode("rand", pkgPath, nil) - pkg.DefineGoNativeValue("Intn", rand.Intn) - pkg.DefineGoNativeValue("Uint32", rand.Uint32) - pkg.DefineGoNativeValue("Seed", rand.Seed) - pkg.DefineGoNativeValue("New", rand.New) - pkg.DefineGoNativeValue("NewSource", rand.NewSource) - pkg.DefineGoNativeType(reflect.TypeOf(rand.Rand{})) + // make native rand same as gno rand. + rnd := rand.New(rand.NewPCG(0, 0)) //nolint:gosec + pkg.DefineGoNativeValue("IntN", rnd.IntN) + pkg.DefineGoNativeValue("Uint32", rnd.Uint32) return pkg, pkg.NewPackage() case "crypto/rand": pkg := gno.NewPackageNode("rand", pkgPath, nil) diff --git a/go.mod b/go.mod index 1bc7c90e2e1..a7f0d4b0ca4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno -go 1.21 +go 1.22 require ( dario.cat/mergo v1.0.0 diff --git a/misc/autocounterd/go.mod b/misc/autocounterd/go.mod index d9d26eab37a..c879f980d10 100644 --- a/misc/autocounterd/go.mod +++ b/misc/autocounterd/go.mod @@ -1,6 +1,6 @@ module loop -go 1.21 +go 1.22 require github.com/gnolang/gno v0.0.0-20240125181217-b6193518e278 diff --git a/misc/devdeps/go.mod b/misc/devdeps/go.mod index 3a1f87c0664..230c6b74686 100644 --- a/misc/devdeps/go.mod +++ b/misc/devdeps/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/misc/devdeps -go 1.21 +go 1.22 require ( github.com/golangci/golangci-lint v1.54.2 // sync with github action diff --git a/misc/docs-linter/go.mod b/misc/docs-linter/go.mod index 2c2840e7a6d..497d4aa8191 100644 --- a/misc/docs-linter/go.mod +++ b/misc/docs-linter/go.mod @@ -1,6 +1,6 @@ module linter -go 1.21.6 +go 1.22 require ( github.com/gnolang/gno v0.0.0-20240516161351-0c9849a8ef0c diff --git a/misc/loop/go.mod b/misc/loop/go.mod index edf64092736..59aac48fac8 100644 --- a/misc/loop/go.mod +++ b/misc/loop/go.mod @@ -1,6 +1,6 @@ module loop -go 1.21 +go 1.22 require ( github.com/docker/docker v24.0.7+incompatible diff --git a/tm2/pkg/libtm/README.md b/tm2/pkg/libtm/README.md index d55a4cad305..bf58440e85e 100644 --- a/tm2/pkg/libtm/README.md +++ b/tm2/pkg/libtm/README.md @@ -45,7 +45,7 @@ To get up and running with the `libtm` package, you can add it to your project u go get -u github.com/gnolang/libtm ``` -Currently, the minimum required go version is `go 1.21`. +Currently, the minimum required go version is `go 1.22`. ## Usage Examples diff --git a/tm2/pkg/libtm/go.mod b/tm2/pkg/libtm/go.mod index a611b2a4454..2aa405a96d3 100644 --- a/tm2/pkg/libtm/go.mod +++ b/tm2/pkg/libtm/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/libtm -go 1.21 +go 1.22 require ( github.com/rs/xid v1.5.0