From ddb4d103201491c41a4baf450fb16f85fbde5095 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:53:25 +0200 Subject: [PATCH] Revert "feat(stdlibs): add math/rand (#2455)" This reverts commit f547d7dcac11de7937664413497fed35eb1a03ff. --- .github/workflows/docs-linter.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/pcg.gno | 121 ------- gnovm/stdlibs/math/rand/pcg_test.gno | 78 ---- gnovm/stdlibs/math/rand/rand.gno | 340 ------------------ 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 +- 39 files changed, 372 insertions(+), 764 deletions(-) create mode 100644 examples/gno.land/p/demo/rand/gno.mod create mode 100644 examples/gno.land/p/demo/rand/rand.gno create mode 100644 examples/gno.land/p/demo/rand/rand0_filetest.gno create mode 100644 examples/gno.land/p/demo/rand/rand_test.gno delete mode 100644 gnovm/stdlibs/math/rand/auto_test.gno delete mode 100644 gnovm/stdlibs/math/rand/pcg.gno delete mode 100644 gnovm/stdlibs/math/rand/pcg_test.gno delete mode 100644 gnovm/stdlibs/math/rand/rand.gno delete mode 100644 gnovm/stdlibs/math/rand/zipf.gno diff --git a/.github/workflows/docs-linter.yml b/.github/workflows/docs-linter.yml index e56c1a663ea..0ffa67dfe95 100644 --- a/.github/workflows/docs-linter.yml +++ b/.github/workflows/docs-linter.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - 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 + run: make -C docs/ lint \ No newline at end of file diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index d11710344b1..cea52e74cd4 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -20,6 +20,7 @@ jobs: fail-fast: false matrix: goversion: + - "1.21.x" - "1.22.x" runs-on: ubuntu-latest timeout-minutes: 30 @@ -35,6 +36,7 @@ jobs: fail-fast: false matrix: goversion: + - "1.21.x" - "1.22.x" # unittests: TODO: matrix with contracts runs-on: ubuntu-latest @@ -58,6 +60,7 @@ jobs: fail-fast: false matrix: goversion: + - "1.21.x" - "1.22.x" # unittests: TODO: matrix with contracts runs-on: ubuntu-latest @@ -105,4 +108,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) + git diff --exit-code || (echo "Some gno.mod files are not tidy, please run 'make tidy'." && exit 1) \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc125a6da73..e041ab18875 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.22+) +- Go (version 1.21+) - 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 6d2c7a34293..ec82f09e467 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnodev -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/contribs/gnofaucet/go.mod b/contribs/gnofaucet/go.mod index e4b63c7a9e9..dc5267fff2b 100644 --- a/contribs/gnofaucet/go.mod +++ b/contribs/gnofaucet/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnofaucet -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod index 711cafed241..d9e785f5226 100644 --- a/contribs/gnokeykc/go.mod +++ b/contribs/gnokeykc/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnokeykc -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/contribs/gnomd/go.mod b/contribs/gnomd/go.mod index 8bc352d4848..39043bae144 100644 --- a/contribs/gnomd/go.mod +++ b/contribs/gnomd/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/contribs/gnomd -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/docs/getting-started/local-setup/installation.md b/docs/getting-started/local-setup/installation.md index a3658fa6ab3..8700ff9a2b2 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.22+** +- **Go 1.21+** - **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 f1acf06049f..a1a337a5a48 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.22+** +- **Go 1.21+** - **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. +Your new Gno node should be up and running, and syncing block data from the remote chain. \ No newline at end of file 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 8f94037dc1d..1927679db8f 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.22+** +- **Go 1.21+** - **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 a2f83f2bbc6..89ad4f7b990 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 | `full`[^9] | +| math/rand | `todo` | | mime | `tbd` | | mime/multipart | `tbd` | | mime/quotedprintable | `tbd` | @@ -291,7 +291,6 @@ 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 new file mode 100644 index 00000000000..098af152648 --- /dev/null +++ b/examples/gno.land/p/demo/rand/gno.mod @@ -0,0 +1,3 @@ +// 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 new file mode 100644 index 00000000000..2fa16d627be --- /dev/null +++ b/examples/gno.land/p/demo/rand/rand.gno @@ -0,0 +1,139 @@ +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 new file mode 100644 index 00000000000..446e04b696d --- /dev/null +++ b/examples/gno.land/p/demo/rand/rand0_filetest.gno @@ -0,0 +1,56 @@ +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 new file mode 100644 index 00000000000..2651f0af089 --- /dev/null +++ b/examples/gno.land/p/demo/rand/rand_test.gno @@ -0,0 +1,49 @@ +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 f2d3ddebadc..6276629cba2 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,3 +1,8 @@ +// Draft + module gno.land/r/demo/art/gnoface -require gno.land/p/demo/ufmt v0.0.0-latest +require ( + gno.land/p/demo/rand v0.0.0-latest + 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 9e85c5c7387..95493b52bf5 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 := uint64(std.GetHeight()) + seed := std.GetHeight() path = strings.TrimSpace(path) if path != "" { @@ -18,7 +18,7 @@ func Render(path string) string { if err != nil { panic(err) } - seed = uint64(s) + seed = int64(s) } output := ufmt.Sprintf("Gnoface #%d\n", seed) @@ -26,7 +26,7 @@ func Render(path string) string { return output } -func Draw(seed uint64) string { +func Draw(seed int64) string { var ( hairs = []string{ " s", @@ -102,7 +102,7 @@ func Draw(seed uint64) string { } ) - r := rand.New(rand.NewPCG(seed, 0xdeadbeef)) + r := rand.FromSeed(seed) return pick(r, hairs) + "\n" + pick(r, headtop) + "\n" + @@ -117,8 +117,8 @@ func Draw(seed uint64) string { pick(r, headbottom) + "\n" } -func pick(r *rand.Rand, slice []string) string { - return slice[r.IntN(len(slice))] +func pick(r *rand.Instance, 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 e82bd819483..630cce85c55 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 uint64 + seed int64 expected string }{ { seed: 42, expected: ` ||||||| - ||||||||| + ////////\ | | - | . ~ | -)| v v |O + | ~ . | +)| X X |. | | - | L | + | C | | | - | ___ | + | __/ | | | \~~~~~~~/ `[1:], @@ -30,31 +30,31 @@ func TestDraw(t *testing.T) { { seed: 1337, expected: ` - ....... - ||||||||| + s + /|||||||\ | | - | . _ | -D| x X |O + | . * | +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```" + ` ||||||| - ||||||||| + ////////\ | | - | . ~ | -)| v v |O + | ~ . | +)| X X |. | | - | L | + | C | | | - | ___ | + | __/ | | | \~~~~~~~/ ` + "```\n", @@ -95,31 +95,31 @@ func TestRender(t *testing.T) { { path: "1337", expected: "Gnoface #1337\n```" + ` - ....... - ||||||||| + s + /|||||||\ | | - | . _ | -D| x X |O + | . * | +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 7044f0f72b3..9804aecc7f1 100644 --- a/examples/gno.land/r/x/manfred_outfmt/gno.mod +++ b/examples/gno.land/r/x/manfred_outfmt/gno.mod @@ -2,4 +2,7 @@ module gno.land/r/x/manfred_outfmt -require gno.land/p/demo/ufmt v0.0.0-latest +require ( + gno.land/p/demo/rand v0.0.0-latest + 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 01981024189..5468a65c06f 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,21 +27,22 @@ func (res *Result) String() string { return output } -var rSeed = rand.NewPCG(0, 0) +var rSeed int64 func genResult() Result { - r := rand.New(rSeed) - // init rand + r := rand.FromSeed(rSeed) + defer func() { rSeed = r.Seed() }() + 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 69c07bbbf16..a60aeb384db 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.Errorf("expected %q, got %q.", expected, got) + t.Fatalf("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: 222 -Numbers: 34 44 39 7 72 48 74 +Number: 957 +Numbers: 3 54 32 88 ` if got != expected { - t.Errorf("expected %q, got %q.", expected, got) + t.Fatalf("expected %q, got %q.", expected, got) } } @@ -35,11 +35,11 @@ Numbers: 34 44 39 7 72 48 74 { got := outfmt.Render("?fmt=stringer") expected := `Text: Hello Gnomes! -Number: 898 -Numbers: 24 25 2 +Number: 141 +Numbers: 98 27 ` if got != expected { - t.Errorf("expected %q, got %q.", expected, got) + t.Fatalf("expected %q, got %q.", expected, got) } } @@ -47,18 +47,18 @@ Numbers: 24 25 2 { got := outfmt.Render("?fmt=json") - expected := `{"Number":746,"Text":"Hello Gnomes!","Numbers":[57,82,16,14,28,32]}` + expected := `{"Number":801,"Text":"Hello Gnomes!","Numbers":[5,78,51,78,91,41]}` if got != expected { - t.Errorf("expected %q, got %q.", expected, got) + t.Fatalf("expected %q, got %q.", expected, got) } } // jsonp { got := outfmt.Render("?fmt=jsonp") - expected := `callback({"Number":795,"Text":"Hello Gnomes!","Numbers":[29,51,88,61,93,21,2,66,79]})` + expected := `callback({"Number":63,"Text":"Hello Gnomes!","Numbers":[2,66,50,73,81]})` if got != expected { - t.Errorf("expected %q, got %q.", expected, got) + t.Fatalf("expected %q, got %q.", expected, got) } } } diff --git a/gnovm/stdlibs/bytes/buffer_test.gno b/gnovm/stdlibs/bytes/buffer_test.gno index 601901955cd..a8837494224 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 927f89c5559..c7762f2f67b 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 deleted file mode 100644 index 5945039ae18..00000000000 --- a/gnovm/stdlibs/math/rand/auto_test.gno +++ /dev/null @@ -1,39 +0,0 @@ -// 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/pcg.gno b/gnovm/stdlibs/math/rand/pcg.gno deleted file mode 100644 index 77708d799e2..00000000000 --- a/gnovm/stdlibs/math/rand/pcg.gno +++ /dev/null @@ -1,121 +0,0 @@ -// 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 deleted file mode 100644 index 843506d7234..00000000000 --- a/gnovm/stdlibs/math/rand/pcg_test.gno +++ /dev/null @@ -1,78 +0,0 @@ -// 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 deleted file mode 100644 index 250ea7c5a05..00000000000 --- a/gnovm/stdlibs/math/rand/rand.gno +++ /dev/null @@ -1,340 +0,0 @@ -// 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/zipf.gno b/gnovm/stdlibs/math/rand/zipf.gno deleted file mode 100644 index f04c814eb75..00000000000 --- a/gnovm/stdlibs/math/rand/zipf.gno +++ /dev/null @@ -1,77 +0,0 @@ -// 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 dac6ab11f67..8416f0b77e1 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.NewPCG(0, 0)), + r: rand.New(rand.NewSource(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 01aa3f47da1..cfed6498051 100644 --- a/gnovm/tests/files/extern/p1/s2.gno +++ b/gnovm/tests/files/extern/p1/s2.gno @@ -3,3 +3,5 @@ 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 91364f45389..dc52adafc31 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: 956301160 +// num: 2596996162 diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index 86c81be9a18..3dbd292ea68 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -23,7 +23,7 @@ import ( "log" "math" "math/big" - "math/rand/v2" + "math/rand" "net" "net/url" "os" @@ -114,6 +114,7 @@ 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 { @@ -270,10 +271,12 @@ 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) - // 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) + 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{})) return pkg, pkg.NewPackage() case "crypto/rand": pkg := gno.NewPackageNode("rand", pkgPath, nil) diff --git a/go.mod b/go.mod index d2b4a5f9825..30a7f4dd84c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/misc/autocounterd/go.mod b/misc/autocounterd/go.mod index 12297e3c6ca..0236341ccc3 100644 --- a/misc/autocounterd/go.mod +++ b/misc/autocounterd/go.mod @@ -1,6 +1,6 @@ module loop -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/misc/devdeps/go.mod b/misc/devdeps/go.mod index e0cb385020e..25d9a218cf7 100644 --- a/misc/devdeps/go.mod +++ b/misc/devdeps/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/gno/misc/devdeps -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/misc/docs-linter/go.mod b/misc/docs-linter/go.mod index be771c9a952..b92418db3dd 100644 --- a/misc/docs-linter/go.mod +++ b/misc/docs-linter/go.mod @@ -1,6 +1,6 @@ module linter -go 1.22 +go 1.21.6 toolchain go1.22.4 diff --git a/misc/loop/go.mod b/misc/loop/go.mod index 91d0d6cdf34..4bb42f103e4 100644 --- a/misc/loop/go.mod +++ b/misc/loop/go.mod @@ -1,6 +1,6 @@ module loop -go 1.22 +go 1.21 toolchain go1.22.4 diff --git a/tm2/pkg/libtm/README.md b/tm2/pkg/libtm/README.md index bf58440e85e..d55a4cad305 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.22`. +Currently, the minimum required go version is `go 1.21`. ## Usage Examples diff --git a/tm2/pkg/libtm/go.mod b/tm2/pkg/libtm/go.mod index e903dd664e6..a8f80c76e06 100644 --- a/tm2/pkg/libtm/go.mod +++ b/tm2/pkg/libtm/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/libtm -go 1.22 +go 1.21 toolchain go1.22.4